From f8ff9a540c871e96f6e4d8f6573010993717e90b Mon Sep 17 00:00:00 2001 From: siva01 Date: Mon, 17 Aug 2026 16:16:52 +0200 Subject: [PATCH 1/3] security: untrack internal topology from public repo MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit clawbot is a public repository, but two workspace files published the private ecosystem's internal layout. TOOLS.md was a complete map of six containers, their ports and the shared bridge network they sit on. todo.md is an internal build roadmap carrying absolute local filesystem paths into every private repo, internal auth routes, .local hostnames and MCP token variable names. Neither is a credential — .mcp.json correctly uses ${MCP_GATEWAY_TOKEN} substitution and no secret was ever committed — but together they hand an attacker the internal topology for free. Both are gitignored rather than deleted: workspace/ is bind-mounted (see docker-compose.yml), not baked into the image, so the agent keeps reading its local copies and only the tracking changes. TOOLS.md.example ships the same structure with placeholder container names. Skill docs are left alone. They name sibling projects, but the names alone expose no topology and are already public via README — stripping them would gut the agent's functional documentation for no security gain. Co-Authored-By: Claude Opus 5 --- .gitignore | 7 + AGENTS.md | 3 +- .../workspace/{TOOLS.md => TOOLS.md.example} | 33 ++-- ironclaw/workspace/todo.md | 164 ------------------ 4 files changed, 29 insertions(+), 178 deletions(-) rename ironclaw/workspace/{TOOLS.md => TOOLS.md.example} (52%) delete mode 100644 ironclaw/workspace/todo.md diff --git a/.gitignore b/.gitignore index e7c162c..7eb4b25 100755 --- a/.gitignore +++ b/.gitignore @@ -12,3 +12,10 @@ openclaw/workspace/.openclaw/ services/linkedin/chromadb/ services/linkedin/cookies/ services/linkedin/posts.json + +# Runtime ecosystem map — real container names/ports; this repo is PUBLIC. +# Bind-mounted from disk (see docker-compose.yml), so it never needs to be tracked. +# Track ironclaw/workspace/TOOLS.md.example instead. +ironclaw/workspace/TOOLS.md +# Internal build roadmap — absolute local paths into private repos, auth routes, token names +ironclaw/workspace/todo.md diff --git a/AGENTS.md b/AGENTS.md index 8eea08b..61b96ca 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -17,7 +17,8 @@ clawbot/ │ ├── .mcp.json # MCP server connections (Drupal, Apify, etc.) │ ├── IDENTITY.md # Agent name, persona, avatar │ ├── SOUL.md # Agent values and behavioural guidelines -│ ├── TOOLS.md # Available tools and how to use them +│ ├── TOOLS.md # Available tools and how to use them — gitignored (real container +│ │ # names); copy from TOOLS.md.example │ ├── USER.md # Info about the user (fill in as needed) │ └── skills/ # Custom skill playbooks (SKILL.md per skill) ├── docker-compose.yml # Main Compose file diff --git a/ironclaw/workspace/TOOLS.md b/ironclaw/workspace/TOOLS.md.example similarity index 52% rename from ironclaw/workspace/TOOLS.md rename to ironclaw/workspace/TOOLS.md.example index 748d8c2..8e3d6b8 100644 --- a/ironclaw/workspace/TOOLS.md +++ b/ironclaw/workspace/TOOLS.md.example @@ -1,15 +1,22 @@ # TOOLS.md - Ecosystem Map +> **Template.** Copy to `TOOLS.md` and replace the placeholder container names and ports with +> your actual ones: `cp TOOLS.md.example TOOLS.md` +> +> `TOOLS.md` is gitignored on purpose — this repo is public, and the real ecosystem map is +> internal infrastructure detail. The workspace is bind-mounted, so the agent reads your local +> copy directly; a `docker compose restart ironclaw` picks up changes. + ## MCP Servers | Server | Container | Port | Tools | Use For | |---|---|---|---|---| -| **`gateway`** | mcpserver-gateway | 8100 | 29 | **Unified entry point — all tools via single auth** | -| **`site-files`** | clawbot-openclaw-1 (local) | — | ~10 | **Read/write Hugo source at `/site`** | -| `ragchat` | ragchat-nginx-1 | 8080 | 21 | CMS content, RAG knowledge base, Drupal | -| `seobot` | seo-tools-mcp | 3001 | 3 | Website SEO audits, crawl reports | -| `osintbot` | osintbot-mcp | 8200 | — | LinkedIn (Apify)/OSINT research, prospect intel | -| `sales` | sales-assistant-assistant-1 | 8000 | 3 | Product Q&A, knowledge base search | +| **`gateway`** | mcp-gateway | 8100 | 29 | **Unified entry point — all tools via single auth** | +| **`site-files`** | (local) | — | ~10 | **Read/write Hugo source at `/site`** | +| `ragchat` | ragchat | 8080 | 21 | CMS content, RAG knowledge base, Drupal | +| `seobot` | seo-mcp | 3001 | 3 | Website SEO audits, crawl reports | +| `osintbot` | osint-mcp | 8200 | — | LinkedIn (Apify)/OSINT research, prospect intel | +| `sales` | sales-api | 8000 | 3 | Product Q&A, knowledge base search | ### Gateway tool naming @@ -36,15 +43,15 @@ Tools routed through the gateway use `{server}__{tool}` format, e.g.: ## Docker Network -All containers on `nginx-proxy` bridge network. -OpenClaw container: `clawbot-openclaw-1`. +All containers on a shared bridge network (see `docker-compose.yml`). +OpenClaw container: ``. Reach services by container name over the network. ## Key URLs - OpenClaw dashboard: http://127.0.0.1:18790 -- **MCP Gateway: http://mcpserver-gateway:8100** (internal) / http://127.0.0.1:8100 (host) -- RagChat: http://ragchat-nginx-1:8080 -- Sales assistant: http://sales-assistant-assistant-1:8000 -- SEO crawler MCP: http://seo-tools-mcp:3001 -- OSINT MCP: http://osintbot-mcp:8200 +- **MCP Gateway: http://mcp-gateway:8100** (internal) / http://127.0.0.1:8100 (host) +- RagChat: http://ragchat:8080 +- Sales assistant: http://sales-api:8000 +- SEO crawler MCP: http://seo-mcp:3001 +- OSINT MCP: http://osint-mcp:8200 diff --git a/ironclaw/workspace/todo.md b/ironclaw/workspace/todo.md deleted file mode 100644 index 3254518..0000000 --- a/ironclaw/workspace/todo.md +++ /dev/null @@ -1,164 +0,0 @@ -# Agentic Ops & lkv.cz — Master Project TODO List - -This document acts as the consolidated tracking system for the **Agentic Ops** roadmap, microsite ideas, and business planning decisions. It highlights what is **Done** (implemented in code) and what **Has to be Done** (configuration, runtime setup, and GTM steps). - ---- - -## 📊 High-Level Status Dashboard - -| Phase | Component / Goal | Status | Key Code References | -|---|---|---|---| -| **Phase 0** | Nginx Reverse Proxy & Network | ⚠️ **Partial** (Stopped) | [ops-proxy/docker-compose.yml](file:///home/siva01/projects/lkv/ops-proxy/docker-compose.yml) | -| **Phase 1** | Specialist MCP Servers (ragchat, seo, osint, sales) | **Done** | [mcp-server.ts](file:///home/siva01/projects/lkv/seo-tools/src/mcp-server.ts), [osintbot_mcp.py](file:///home/siva01/projects/lkv/osintbot/scripts/osintbot_mcp.py), [mcp.py](file:///home/siva01/projects/lkv/sales-assistant/src/assistant/api/routes/mcp.py) | -| **Phase 2** | MCP Gateway Aggregator | **Done** | [main.py](file:///home/siva01/projects/lkv/mcpserver/gateway/main.py) | -| **Phase 3** | OpenClaw Integration | ⚠️ **Partial** | [openclaw/.mcp.json](file:///home/siva01/projects/lkv/clawbot/openclaw/workspace/.mcp.json) | -| **Phase 4** | Playbook Skills Definition | **Done** | [weekly-seo-audit](file:///home/siva01/projects/lkv/clawbot/openclaw/workspace/skills/weekly-seo-audit/SKILL.md), [competitor-monitoring](file:///home/siva01/projects/lkv/clawbot/openclaw/workspace/skills/competitor-monitoring/SKILL.md) | -| **Phase 5** | Agent Identity & Guidelines | **Done** | [IDENTITY.md](file:///home/siva01/projects/lkv/clawbot/openclaw/workspace/IDENTITY.md), [SOUL.md](file:///home/siva01/projects/lkv/clawbot/openclaw/workspace/SOUL.md) | -| **Phase 6** | Observability & Alerting | ⚠️ **Partial** | [gateway/main.py](file:///home/siva01/projects/lkv/mcpserver/gateway/main.py), [ops-proxy/conf.d](file:///home/siva01/projects/lkv/ops-proxy/conf.d) | -| **Phase 7** | Marketing Dogfood & Microsites | 📅 **Todo** | Shared Hugo design | -| **Phase 8** | GTM Business Plan & Monetization | 📅 **Todo** | Payments, auth, cost optimization | - ---- - -## 🛠️ Detailed Task Checklist - -### PHASE 0 — nginx-proxy: shared reverse-proxy and network -> **Goal:** Run the central gateway routing all traffic to the containerized backends. - -- [ ] **0.1 — Start nginx-proxy (ops-proxy)** - - [ ] Confirm the external network exists; create it if not: - ```bash - docker network inspect agentic-ops >/dev/null 2>&1 || docker network create -d bridge agentic-ops - ``` - - [ ] Copy env template and configure in `ops-proxy`: - ```bash - cd ops-proxy && cp .env.dist .env - # Set VIRTUAL_HOST and DEFAULT_EMAIL in .env - ``` - - [ ] Start the proxy stack: `docker compose up -d` - - [ ] Verify: `docker ps` shows `ops-proxy` and `ops-acme` containers running. -- [x] **0.2 — Register each service as a virtual host** - - [x] Map `ragchat` to `ragchat.local` (Port 8080) -> [ragchat/docker-compose.yml](file:///home/siva01/projects/lkv/ragchat/docker-compose.yml) - - [x] Map `sales-assistant` to `dev.ludekkvapil.cz` (Port 8000) -> [sales-assistant/docker-compose.yml](file:///home/siva01/projects/lkv/sales-assistant/docker-compose.yml) - - [x] Map `mcpserver` static site to `mcpserver.local` (Port 80) -> [mcpserver/docker-compose.yml](file:///home/siva01/projects/lkv/mcpserver/docker-compose.yml) - - [x] Map `mcpserver-gateway` to `mcp-gateway.local` (Port 8100) -> [mcpserver/docker-compose.yml](file:///home/siva01/projects/lkv/mcpserver/docker-compose.yml) - - [ ] Map `seo-tools` MCP to `seo.local` (Port 3001) -> **Todo:** Add `VIRTUAL_HOST` variables to [seo-tools/docker-compose.yml](file:///home/siva01/projects/lkv/seo-tools/docker-compose.yml) - - [ ] Map `osintbot` MCP (`osintbot-mcp`, Port 8200) — internal only, reached via mcpserver gateway; no public VIRTUAL_HOST needed -> [osintbot/docker-compose.yml](file:///home/siva01/projects/lkv/osintbot/docker-compose.yml) - - [ ] Map `clawbot` OpenClaw dashboard to `clawbot.local` (Port 18789) -> **Todo:** Add `VIRTUAL_HOST` variables to [clawbot/docker-compose.yml](file:///home/siva01/projects/lkv/clawbot/docker-compose.yml) -- [ ] **0.3 — Local /etc/hosts entries (dev environment)** - - [ ] Add domain mapping in host `/etc/hosts`: - ``` - 127.0.0.1 ragchat.local dev.ludekkvapil.cz mcpserver.local mcp-gateway.local seo.local osint.local clawbot.local - ``` -- [ ] **0.4 — Shared secrets file** - - [ ] Create `.env.shared` at workspace root with all MCP tokens (e.g. `RAGCHAT_MCP_TOKEN`, `SEO_MCP_TOKEN`, `OSINT_MCP_TOKEN`, `SALES_MCP_TOKEN`, `MCP_GATEWAY_TOKEN`). - - [ ] Reference `.env.shared` from each project's docker-compose file via `env_file`. - ---- - -### PHASE 1 — Expose each specialist as an MCP server -> **Goal:** Expose JSON-RPC 2.0 endpoints for all specialty applications to communicate with the orchestrator. - -- [x] **1.1 — ragchat: register existing MCP endpoint** - - Exposes route `/mcp/post` via custom Drupal module `mcp_tools` requiring token authentication. -- [x] **1.2 — seo-tools: add MCP HTTP wrapper** - - Implemented in [mcp-server.ts](file:///home/siva01/projects/lkv/seo-tools/src/mcp-server.ts). Exposes `crawl`, `get_report`, and `list_reports`. -- [x] **1.3 — OSINT: add MCP HTTP endpoint** - - Implemented in [osintbot_mcp.py](file:///home/siva01/projects/lkv/osintbot/scripts/osintbot_mcp.py) (fastmcp tools), exposed over HTTP via `osintbot_mcp_http.py` shim. Exposes `osintbot_investigate`, `osintbot_plan`, `osintbot_wiki_lookup`, `osintbot_status`, etc. -- [x] **1.4 — sales-assistant: expose chat and product tools** - - Implemented in [mcp.py](file:///home/siva01/projects/lkv/sales-assistant/src/assistant/api/routes/mcp.py). Exposes `chat`, `search_knowledge`, and `get_status`. - ---- - -### PHASE 2 — MCPserver as unified MCP gateway -> **Goal:** Aggregate all four specialist MCP servers under a single namespaced gateway. - -- [x] **2.1 — Build FastAPI aggregator service** - - Implemented in [main.py](file:///home/siva01/projects/lkv/mcpserver/gateway/main.py). It merges tool manifests and namespaces tools as `{server}__{tool}` (e.g., `seobot__crawl`, `ragchat__ProductSearch`). -- [x] **2.2 — Rate limiting and proxy buffering** - - Configured in [vmweb-limits.conf](file:///home/siva01/projects/lkv/ops-proxy/conf.d/vmweb-limits.conf) to support large JSON-RPC packages. -- [ ] **2.3 — Test gateway aggregation** - - Run `curl -H "Authorization: Bearer $MCP_GATEWAY_TOKEN" http://mcp-gateway.local/mcp/post` with `tools/list` request and confirm namespaced tools are fetched and merged correctly. - ---- - -### PHASE 3 — Wire OpenClaw (clawbot) to all specialists -> **Goal:** Configure ClawBot to utilize the unified gateway or connect to all backends. - -- [ ] **3.1 — Update ClawBot MCP configurations** - - Edit [openclaw/.mcp.json](file:///home/siva01/projects/lkv/clawbot/openclaw/workspace/.mcp.json) to reference the aggregated gateway URL `http://mcp-gateway.local/mcp/post` (or point directly to all 4 backends). -- [ ] **3.2 — Restart OpenClaw & verify tools** - - Restart the service: `docker compose restart openclaw` - - Verify that the namespaced tools appear on the OpenClaw dashboard. -- [ ] **3.3 — Update tools documentation** - - Update [TOOLS.md](file:///home/siva01/projects/lkv/clawbot/openclaw/workspace/TOOLS.md) with details on namespaced tools. - ---- - -### PHASE 4 — Define agent workflows / playbooks -> **Goal:** Implement agent execution instructions (skills) in markdown files. - -- [x] **4.1 — Implement `weekly-seo-audit` playbook** - - Created at [weekly-seo-audit/SKILL.md](file:///home/siva01/projects/lkv/clawbot/openclaw/workspace/skills/weekly-seo-audit/SKILL.md). -- [x] **4.2 — Implement `competitor-monitoring` playbook** - - Created at [competitor-monitoring/SKILL.md](file:///home/siva01/projects/lkv/clawbot/openclaw/workspace/skills/competitor-monitoring/SKILL.md). -- [x] **4.3 — Implement `support-escalation-review` playbook** - - Created at [support-escalation-review/SKILL.md](file:///home/siva01/projects/lkv/clawbot/openclaw/workspace/skills/support-escalation-review/SKILL.md). -- [x] **4.4 — Implement `new-client-onboarding` playbook** - - Created at [new-client-onboarding/SKILL.md](file:///home/siva01/projects/lkv/clawbot/openclaw/workspace/skills/new-client-onboarding/SKILL.md). - ---- - -### PHASE 5 — Identity, persona, and memory -> **Goal:** Setup agent profile, values, system memory, and heartbeat checklist. - -- [x] **5.1 — Configure Identity & Persona** - - Configured at [IDENTITY.md](file:///home/siva01/projects/lkv/clawbot/openclaw/workspace/IDENTITY.md) (Name: ClawBot, Role: Agentic Ops Manager). -- [x] **5.2 — Configure Soul & Boundaries** - - Configured at [SOUL.md](file:///home/siva01/projects/lkv/clawbot/openclaw/workspace/SOUL.md). -- [x] **5.3 — Configure User memory & Bootstrap sequences** - - Configured at [USER.md](file:///home/siva01/projects/lkv/clawbot/openclaw/workspace/USER.md) and [HEARTBEAT.md](file:///home/siva01/projects/lkv/clawbot/openclaw/workspace/HEARTBEAT.md). - ---- - -### PHASE 6 — Monitoring and observability -> **Goal:** Instrument request logging and alerting across the agentic services. - -- [x] **6.1 — Structured logging** - - Gateway [main.py](file:///home/siva01/projects/lkv/mcpserver/gateway/main.py) writes JSON logs indicating tool, duration, and response codes. -- [ ] **6.2 — Dashboard and Centralized Log Storage** - - Store request metrics under `mcpserver/logs/` and configure log routing. -- [ ] **6.3 — Setup crawl fail alerts** - - Set up warning notifications if a crawl job fails repeatedly. - ---- - -### PHASE 7 — Marketing dogfood loop & microsites -> **Goal:** Configure ClawBot to audit the local site and deploy client-facing microsites. - -- [ ] **7.1 — Weekly audit against `ludekkvapil.cz`** - - Trigger weekly cron skill to audit the owner's Hugo static website. -- [ ] **7.2 — Build Microsites using Hugo (Shared Theme / Unified Design)** - - [ ] **Microsite `seo.ludekkvapil.cz`**: Form/interface for generating SEO reports leveraging `seo-tools`. - - [ ] **Microsite `courses.ludekkvapil.cz`**: Course registry, inspired by [naucmese.cz/ludek-kvapil](https://www.naucmese.cz/ludek-kvapil). - - [ ] **Microsite `osint.ludekkvapil.cz`**: OSINT intelligence interface leveraging `osintbot`. -- [ ] **7.3 — Competitor Feed draft posts** - - Run daily OSINT checks on competitor moves and write draft recommendations into `ludekkvapil/content/` for human review. - ---- - -### PHASE 8 — First paying client (GTM Business Plan) -> **Goal:** Turn the unified agent crew into a commercial SaaS offer. - -- [ ] **8.1 — Domain & SSL Configuration** - - Setup DNS record and nginx configuration to resolve `seo.ludekkvapil.cz` to the frontend dashboard. -- [ ] **8.2 — Authentication & Authorization** - - Secure the client portal and API endpoints with user authentication. -- [ ] **8.3 — Pricing & Tier Structure** - - Define product pricing: **Per report** vs **Subscription-based**. - - Configure credit allotment, trial limits, and packages (e.g. Free trial limit, monthly subscription quota). -- [ ] **8.4 — Cost Analysis and Optimization** - - [ ] Calculate cost per report (single vs multiple languages, site crawl depth/page size). - - [ ] Optimize LLM token usage: determine optimal model fit (e.g., GPT-4o vs Claude 3.5 Sonnet vs open source models like Llama 3 running locally on Ollama). -- [ ] **8.5 — Payment Integration** - - Connect client signups to Stripe / billing gateway. From a75f2b718e09e2479c79fe60e11b8458015ec4bb Mon Sep 17 00:00:00 2001 From: siva01 Date: Sat, 12 Sep 2026 18:36:40 +0200 Subject: [PATCH 2/3] docs(skills): add the ClawBot copy of the Redmine time-tracking playbook MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ClawBot is told to log its hours in SOUL.md and USER.md, but the playbook they point at was never tracked here — the container read it from the bind mount while the repository had no copy of it at all. This copy is deliberately narrower than the canonical one in Luděk's monorepo: this repository is public, so the stack's endpoints and ports, the compose service names and the admin Rails-runner procedure stay out of it, the same way f8ff9a5 untracked TOOLS.md and todo.md. ClawBot loses nothing — it reaches Redmine through the gateway's redmine__* tools, and anything the Agent role can't do needs host access it doesn't have anyway. What it does document, found while using it: the Agent role's writes to status_id and parent_issue_id are dropped silently (Redmine still answers 204), so the playbook requires reading the issue back instead of trusting the response. Co-Authored-By: Claude Opus 5 --- .../skills/redmine-time-tracking/SKILL.md | 159 ++++++++++++++++++ 1 file changed, 159 insertions(+) create mode 100644 ironclaw/workspace/skills/redmine-time-tracking/SKILL.md diff --git a/ironclaw/workspace/skills/redmine-time-tracking/SKILL.md b/ironclaw/workspace/skills/redmine-time-tracking/SKILL.md new file mode 100644 index 0000000..8eabeaa --- /dev/null +++ b/ironclaw/workspace/skills/redmine-time-tracking/SKILL.md @@ -0,0 +1,159 @@ +--- +name: redmine-time-tracking +description: >- + Log the time an agent spends on a project to the self-hosted Redmine (CRM). + Resolve the working directory to a Redmine project, find or create an issue for + the task, then file a time entry under the "AI Agent" activity. Run automatically + after ~15+ minutes of substantive work on a project, or when asked to "log my + time" / "track this work" / "record hours in Redmine". +--- + +# Redmine Time Tracking + +Log agent work time to Redmine. Data model: **Project → Issue → Time entry**. + +> **ClawBot copy.** The canonical version lives in Luděk's monorepo at +> `ai/skills/redmine-time-tracking/SKILL.md`; keep the two in sync when the procedure +> changes. This copy is deliberately narrower — `clawbot` is a public repository, so the +> stack's endpoints and the admin procedure stay in the canonical copy only. You don't need +> them: you reach Redmine through the gateway's `redmine__*` tools. + +## Environment + +- **Tools:** `redmine__` through the MCP gateway. +- **Session start time:** the `WORK SESSION` line you appended to `MEMORY.md`. +- **Clock:** `date -u +%Y-%m-%dT%H:%M:%SZ`; if you have no shell, estimate conservatively + from the conversation. + +## When to run + +- **Automatically**, after roughly 15 minutes or more of real effort on one project + (research, coding, audits, multi-step tasks). This is internal bookkeeping on Luděk's + own infrastructure — you do **not** need to ask permission first. +- On explicit request ("log my time", "track this"). +- On an end-of-day flush: process every open work session that hasn't been logged. + +Skip trivial work (a one-line fix, a quick question) — it's not worth an issue. + +## Procedure + +### 1. Resolve the Redmine project + +Map the working directory to a Redmine project **identifier**: + +| Subproject / area | Redmine identifier | +|---|---| +| `plymovent` | `plymovent` | +| `ragchat` | `ragchat` | +| `osintbot` | `osintbot` | +| `sales-assistant` | `sales-assistant` | +| `mcpserver`, mcpserver.cz site | `mcpserver-cz` | +| `seo-tools` | `seo-tools` | +| `geo` | `geo` | +| `startupfactory` | `startupfactory` | +| `drupal-lama` | `drupal-lama` | +| `ludekkvapil` / `/site` (ludekkvapil.cz) | `ludekkvapil-cz` | +| `clawbot`, `ai`, `redmine`, `docs`, `scripts`, `ops-proxy`, `observability`, `reception`, infra, monorepo root | `lkv` | + +Call `list_redmine_projects` and resolve the mapped identifier to its **numeric `id`** — +`create_redmine_issue` and the time-entry tools want the integer project id, not the +identifier string. If you can't tell which project the work belongs to, **ask** — don't +guess, don't invent a project. + +### 2. Compute hours + +`elapsed = now − session start`, in hours, **rounded to the nearest 0.25, minimum 0.25**. +Count actual working time, not wall-clock time you were idle. + +### 3. Resolve the "AI Agent" activity id + +Call `list_time_entry_activities`, find the entry named **"AI Agent"**, keep its `id`. +If it's missing, the one-time host-side setup hasn't been run — tell Luděk and stop. + +### 4. Find or create the issue (one issue per task) + +- Call `search_redmine_issues` (`query` = a few words from the task) or `list_redmine_issues` + scoped to the project for an **open** issue that already matches this task (you may be + resuming work). If found, use its `id`. +- Otherwise `create_redmine_issue`: + - `project_id`: the **numeric id** from step 1 + - `subject`: a short, specific task title (e.g. *"Fix CLS on blog list pages"*, not *"work"*) + - `description`: 1–3 sentences — what and why + - `fields`: an object with `tracker_id` (from `list_project_trackers` for this project — + use the one that matches the project name), `status_id` for **"In progress"** (look it + up with `list_redmine_issue_statuses`), and `assigned_to_id` = your own user id + (`get_current_user` → `mcp-agent`). + +`status_id` and `parent_issue_id` are usually **dropped silently** — see +[Permission limits of `mcp-agent`](#permission-limits-of-mcp-agent). The issue is still +created; it just lands in the project's default status and unparented. Don't retry, and +don't treat it as a failure of the whole step. + +### 5. File the time entry + +`manage_time_entry`: + - `action`: `"create"` + - `issue_id`: from step 4 + - `hours`: from step 2 + - `activity_id`: the "AI Agent" id from step 3 + - `spent_on`: today (`YYYY-MM-DD`) + - `comments`: one factual sentence on what you did, suffixed with your agent tag — + `… (ClawBot)`. Max 255 chars, no filler. + - Do **not** pass `user_id` — it defaults to the current MCP user (`mcp-agent`). + +### 6. Close the task if it's done + +If the task is complete and produced its deliverable, `update_redmine_issue` to set the +status to **"Done"** and `done_ratio` 100 (pass these in `fields`). Leave it "In progress" +if there's follow-up work. + +Then **read the issue back** and check the status actually changed — the update reports +success even when the status was ignored. If it didn't land, say so in your reply and leave +the status alone; don't loop on it. + +### 7. Record that it's logged + +Change that `WORK SESSION` line's trailing state in `MEMORY.md` to `logged `. + +## Verify + +`list_time_entries` scoped to the project + today — confirm the entry shows user +`mcp-agent`, activity `AI Agent`, and the agent-tag comment suffix. + +Human-readable check, for Luděk: the project's **Time entries** tab, and its **Report** +grouped by Activity — "AI Agent" hours are a separate line from human "Development" / +"Design" hours. + +## Permission limits of `mcp-agent` + +The `mcp-agent` account holds the role **"Agent"**, which can create and edit issues and +file time entries, but **not** manage subtasks or move issues through the workflow. Redmine +does not reject the disallowed attributes — it drops them and still answers `200`/`204`, so +a write can look like it succeeded while nothing changed. Confirmed on 2026-09-12 for: + +| Attribute | What happens | +|---|---| +| `status_id` (on create and update) | ignored; issue keeps the project's default status | +| `parent_issue_id` | ignored; the issue stays top-level | + +What works: `subject`, `description`, `tracker_id`, `assigned_to_id`, `done_ratio`, notes, +and all time-entry writes. + +So: **after any status or parent write, read the issue back**. If it didn't take, report it +in your reply rather than retrying or escalating on your own. + +Anything the Agent role can't do — nesting subtasks, forcing a status, re-running the +setup — needs admin access you don't have. Say what you couldn't do and stop; the host-side +copy of this playbook carries the admin procedure. Lifting the limitation permanently is +Luděk's call: Administration → Roles → Agent → *Manage subtasks*, plus the workflow +transitions for the Agent role per tracker. + +## Notes + +- Never log to a project that isn't in the map above. +- If issue creation fails with a tracker or permission error, the setup script needs to be + (re-)run for that project — report it, don't retry blindly. +- The MCP server authenticates as `mcp-agent` (legacy mode) — you send no credentials. +- The server wraps free-text fields in returned JSON in `` markers as a + prompt-injection guard. That's a display wrapper on reads only — the stored value is + clean; don't include those markers when you write text back. From 56a02ac005d4853f7194ed163b088904fdc3bf1a Mon Sep 17 00:00:00 2001 From: siva01 Date: Mon, 14 Sep 2026 18:32:04 +0200 Subject: [PATCH 3/3] docs(skills): map customers/plymovent to the plymovent project Plymovent moved to lkv/customers/plymovent (Redmine LKV #13), so the directory column has to name the new path or the container resolves nothing and falls through to lkv. Also carries the pending sync of the clawbot and drupal-cz rows, which the canonical copy at ai/skills/redmine-time-tracking/SKILL.md already had. The two copies are required to agree. Refs LKV #13 Co-Authored-By: Claude Opus 5 (1M context) --- ironclaw/workspace/skills/redmine-time-tracking/SKILL.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/ironclaw/workspace/skills/redmine-time-tracking/SKILL.md b/ironclaw/workspace/skills/redmine-time-tracking/SKILL.md index 8eabeaa..1297a68 100644 --- a/ironclaw/workspace/skills/redmine-time-tracking/SKILL.md +++ b/ironclaw/workspace/skills/redmine-time-tracking/SKILL.md @@ -43,7 +43,7 @@ Map the working directory to a Redmine project **identifier**: | Subproject / area | Redmine identifier | |---|---| -| `plymovent` | `plymovent` | +| `customers/plymovent`, `plymovent` | `plymovent` | | `ragchat` | `ragchat` | | `osintbot` | `osintbot` | | `sales-assistant` | `sales-assistant` | @@ -53,7 +53,9 @@ Map the working directory to a Redmine project **identifier**: | `startupfactory` | `startupfactory` | | `drupal-lama` | `drupal-lama` | | `ludekkvapil` / `/site` (ludekkvapil.cz) | `ludekkvapil-cz` | -| `clawbot`, `ai`, `redmine`, `docs`, `scripts`, `ops-proxy`, `observability`, `reception`, infra, monorepo root | `lkv` | +| `clawbot` — you, your workspace and your skills | `clawbot` | +| `customers/drupal-cz`, `drupalcz` (drupal.cz) | `drupal-cz` | +| `ai`, `redmine`, `docs`, `scripts`, `ops-proxy`, `observability`, `reception`, infra, monorepo root | `lkv` | Call `list_redmine_projects` and resolve the mapped identifier to its **numeric `id`** — `create_redmine_issue` and the time-entry tools want the integer project id, not the