Migrate all repos into monorepo context folders

Bahn: aisupport, Analyse-O2C-C2S, awesome-bahn-mcp-servers, beam-mcp,
      Confluence_Bot, db-planet-mcp-server, O2C-Harness, project-audit,
      Projekt-KIQ-HP, teamlandkarte-mcp
Dhive: Jury-Voting
Privat: CV, NoteGraph (NOTE: NoteGraph needs complete redo after consolidation)
Shared: AI-Orchestrator, OrgMyLife, power_skills_and_more
Shared/references: symphony (read-only)

Bahn repos remain available as independent remotes - this monorepo
pulls them in via subtree, the originals are untouched.
This commit is contained in:
2026-06-30 20:39:52 +02:00
parent 2f2b295531
commit a5f8fb49ab
1717 changed files with 447332 additions and 0 deletions
@@ -0,0 +1 @@
{"specId": "1a31d9bb-f189-4374-95b4-1bd117d009b6", "workflowType": "requirements-first", "specType": "feature"}
@@ -0,0 +1,222 @@
# Design Document: Task Name Column
## Overview
This feature adds the `name__c` database column to the Task model and all task-related queries, enabling users to see and look up tasks by their short human-readable name. The changes span four layers:
1. **Model** Add an optional `name` field to the `Task` dataclass.
2. **Database** Modify SQL queries in `TrinoClient` to SELECT `name__c`; add a new `get_task_by_name` method.
3. **Protocol** Extend the `DBClient` protocol with `get_task_by_name`.
4. **MCP Tools** Include the name in list/detail output; resolve name-based lookups in `get_task_details`.
## Architecture
```mermaid
flowchart TD
A[MCP Tool: list_open_tasks] -->|calls| B[TrinoClient.get_open_tasks]
C[MCP Tool: get_task_details] -->|calls| D[TrinoClient.get_task_by_id]
C -->|calls| E[TrinoClient.get_task_by_name]
B --> F[(Trino DB: name__c)]
D --> F
E --> F
B --> G[Task dataclass with name field]
D --> G
E --> G
```
The identifier resolution logic in `get_task_details` will attempt `get_task_by_id` first. If that returns `None`, it falls back to `get_task_by_name`. This keeps backward compatibility for callers passing IDs while enabling name-based lookup without a separate tool.
## Components and Interfaces
### 1. Task Dataclass (`models.py`)
Add a single field:
```python
@dataclass(frozen=True)
class Task:
id: str
name: Optional[str] # NEW maps to name__c
title: str
description: str
start_date: Optional[date]
end_date: Optional[date]
created_date: datetime
skills: list[str] = field(default_factory=list)
```
The field is `Optional[str]` because the database column can be NULL.
### 2. DBClient Protocol (`database/types.py`)
Add one method:
```python
def get_task_by_name(self, name: str) -> Task | None:
"""Fetch one published task by its short name (name__c)."""
raise NotImplementedError
```
### 3. TrinoClient (`database/trino_client.py`)
**SQL changes** Every SELECT that builds a `Task` must include `t.name__c`. Affected methods:
- `get_open_tasks` add `t.name__c` to both the unlimited and CTE-limited queries.
- `get_task_by_id` add `t.name__c` to the SELECT.
**New method** `get_task_by_name`:
```python
def get_task_by_name(self, name: str) -> Optional[Task]:
"""Fetch a single published task by name__c (case-sensitive)."""
query = """
SELECT
t.id,
t.name__c,
t.title__c,
t.description__c,
t.startdate__c,
t.enddate__c,
t.createddate,
s.skillname__c
FROM beschaffungstool_kmp_task_latest t
LEFT JOIN beschaffungstool_kmp_skill_latest s
ON t.id = s.task__c
WHERE t.name__c = ? AND t.status__c = ?
"""
# Same row-assembly logic as get_task_by_id
```
### 4. MCP Server (`mcp_server.py`)
**`list_open_tasks`** Add a "Name" column to the output table (between task_id and Title).
**`get_task_details`** Change the resolution logic:
```python
@mcp.tool()
async def get_task_details(task_id: str) -> str:
# 1. Try by ID
task = db_client.get_task_by_id(task_id)
# 2. Fallback: try by name
if task is None:
task = db_client.get_task_by_name(task_id)
# 3. Not found
if task is None:
return f"Task not found or not published: {task_id}"
# ... rest unchanged, but include task.name in the summary table
```
Add "Name" to the summary table header and row.
## Data Models
### Task Table Schema (relevant columns)
| Column | Type | Nullable | Maps to |
|--------|------|----------|---------|
| `id` | VARCHAR | No | `Task.id` |
| `name__c` | VARCHAR | Yes | `Task.name` |
| `title__c` | VARCHAR | Yes | `Task.title` |
| `description__c` | VARCHAR | Yes | `Task.description` |
| `startdate__c` | DATE | Yes | `Task.start_date` |
| `enddate__c` | DATE | Yes | `Task.end_date` |
| `createddate` | TIMESTAMP | No | `Task.created_date` |
### Task Dataclass (after change)
```python
@dataclass(frozen=True)
class Task:
id: str
name: Optional[str]
title: str
description: str
start_date: Optional[date]
end_date: Optional[date]
created_date: datetime
skills: list[str] = field(default_factory=list)
```
## Correctness Properties
*A property is a characteristic or behavior that should hold true across all valid executions of a system — essentially, a formal statement about what the system should do. Properties serve as the bridge between human-readable specifications and machine-verifiable correctness guarantees.*
### Property 1: Task name field preserves database value
*For any* database row with a `name__c` value (including NULL), when the row is mapped to a `Task` object, `Task.name` must equal the original `name__c` value (with NULL mapped to `None`).
**Validates: Requirements 1.1, 1.2, 1.3, 1.4**
### Property 2: Task output contains name
*For any* `Task` with a non-None `name` field, the formatted output string (from both `list_open_tasks` and `get_task_details`) must contain that name value as a substring.
**Validates: Requirements 2.1, 2.2**
### Property 3: Name lookup returns correct task
*For any* task that exists in the database with a given `name__c` value, calling `get_task_by_name` with that exact name must return a `Task` whose `name` field equals the queried name.
**Validates: Requirements 3.2**
### Property 4: Name resolution equivalence
*For any* published task with a non-None name, calling `get_task_details` with the task's name must produce the same task data as calling `get_task_details` with the task's ID.
**Validates: Requirements 3.4**
### Property 5: Unknown identifier returns not-found
*For any* string that is neither a valid published task ID nor a valid published task name, `get_task_details` must return a not-found message.
**Validates: Requirements 3.3, 3.5**
## Error Handling
| Scenario | Behavior |
|----------|----------|
| `name__c` is NULL in DB | `Task.name` is set to `None`; output displays empty or omits the name cell |
| `get_task_by_name` receives empty string | Returns `None` (no match) |
| `get_task_details` identifier matches neither ID nor name | Returns `"Task not found or not published: {identifier}"` |
| Multiple tasks share the same `name__c` (data quality issue) | `get_task_by_name` returns the first match (ORDER BY createddate DESC) |
The resolution order in `get_task_details` (ID first, then name) ensures that if a name happens to look like an ID, the ID lookup takes precedence. This avoids ambiguity.
## Testing Strategy
### Unit Tests
- Verify `Task` dataclass construction with `name=None` and `name="ABC-123"`.
- Verify `list_open_tasks` output table includes a "Name" column.
- Verify `get_task_details` output table includes the name value.
- Verify `get_task_details` falls back to `get_task_by_name` when ID lookup returns None.
- Verify `get_task_by_name` returns None for empty string input.
### Property-Based Tests
Use **Hypothesis** (Python PBT library) with a minimum of 100 iterations per property.
Each test must be tagged with a comment referencing the design property:
```python
# Feature: task-name-column, Property 1: Task name field preserves database value
```
**Property 1** Generate random `(name__c_value | None)` values, construct Task objects via the row-mapping helper, assert `task.name == input_value`.
**Property 2** Generate random Task objects with non-None names (using `st.text(min_size=1)`), format them through the output functions, assert the name string appears in the output.
**Property 3** Generate a random set of tasks with unique names, mock the DB cursor to return those rows, call `get_task_by_name(name)`, assert the returned task's name matches.
**Property 4** Generate a task with a non-None name, mock both `get_task_by_id` and `get_task_by_name` to return the same task, call `get_task_details` with the name and with the ID, assert both outputs are identical.
**Property 5** Generate random strings, filter out any that match known task IDs or names in the mock data, call `get_task_details`, assert the output contains "not found".
### Test Configuration
```python
from hypothesis import given, settings, strategies as st
@settings(max_examples=100)
```
@@ -0,0 +1,47 @@
# Requirements Document
## Introduction
The Teamlandkarte MCP server currently fetches tasks from the database without including the `name` column (a short identifier). Users need to reference tasks by this short name in addition to the full ID or title. This feature adds the `name` column to all task queries and enables task lookup by name.
## Glossary
- **MCP_Server**: The Teamlandkarte MCP server that exposes tools for querying tasks and capacities.
- **Task**: A published task record stored in the `beschaffungstool_kmp_task_latest` database table.
- **Task_Name**: The `name__c` column in the task table, a short human-readable identifier for a task.
- **DBClient**: The database client protocol that defines methods for fetching tasks and capacities.
- **TrinoClient**: The concrete database client implementation that queries the Trino/Presto backend.
## Requirements
### Requirement 1: Include Task Name in Task Model
**User Story:** As a user, I want the task name (short ID) to be part of the task data, so that I can see and use it when browsing tasks.
#### Acceptance Criteria
1. THE Task model SHALL include a `name` field of type optional string.
2. WHEN the TrinoClient fetches open tasks, THE TrinoClient SHALL select the `name__c` column from the task table and populate the Task `name` field.
3. WHEN the TrinoClient fetches a task by ID, THE TrinoClient SHALL select the `name__c` column from the task table and populate the Task `name` field.
4. WHEN the task name column value is NULL in the database, THE Task `name` field SHALL be set to None.
### Requirement 2: Display Task Name in Output
**User Story:** As a user, I want to see the task name in task listings and detail views, so that I can reference tasks by their short name.
#### Acceptance Criteria
1. WHEN the MCP_Server returns a list of open tasks, THE MCP_Server SHALL include the task name in the output for each task.
2. WHEN the MCP_Server returns task details, THE MCP_Server SHALL include the task name in the summary table.
### Requirement 3: Look Up Task by Name
**User Story:** As a user, I want to retrieve task details by providing only the task name, so that I do not need to remember the full ID.
#### Acceptance Criteria
1. THE DBClient protocol SHALL define a `get_task_by_name` method that accepts a task name string and returns a Task or None.
2. WHEN a valid published task name is provided, THE TrinoClient SHALL return the matching Task with all fields populated.
3. WHEN a task name that does not match any published task is provided, THE TrinoClient SHALL return None.
4. WHEN the user provides a task name to the get_task_details tool, THE MCP_Server SHALL resolve the task using the name and return the full task details.
5. IF the provided identifier matches neither a task ID nor a task name, THEN THE MCP_Server SHALL return a "not found" message.
@@ -0,0 +1,107 @@
# Implementation Plan: Task Name Column
## Overview
Add the `name__c` database column to the Task model and all task queries, enabling users to see and look up tasks by their short human-readable name. Changes span model, database layer, protocol, and MCP tool output.
## Tasks
- [x] 1. Add name field to Task model and update TrinoClient queries
- [x] 1.1 Add `name: Optional[str]` field to the Task dataclass in `models.py`
- Insert `name: Optional[str]` after `id` field
- Field must be Optional since `name__c` can be NULL in the database
- _Requirements: 1.1, 1.4_
- [x] 1.2 Update `get_open_tasks` SQL queries in `trino_client.py` to SELECT `t.name__c`
- Add `t.name__c` to the unlimited query SELECT clause
- Add `t.name__c` to the CTE inner SELECT and outer SELECT in the limited query
- Update row unpacking to extract the name value
- Populate `name` field in Task construction (map NULL to None)
- _Requirements: 1.2, 1.4_
- [x] 1.3 Update `get_task_by_id` SQL query in `trino_client.py` to SELECT `t.name__c`
- Add `t.name__c` to the SELECT clause
- Update row unpacking to extract the name value
- Populate `name` field in Task construction (map NULL to None)
- _Requirements: 1.3, 1.4_
- [ ]* 1.4 Write property test: Task name field preserves database value
- **Property 1: Task name field preserves database value**
- Generate random `(name__c_value | None)` values using Hypothesis
- Construct Task objects via the row-mapping pattern, assert `task.name == input_value`
- **Validates: Requirements 1.1, 1.2, 1.3, 1.4**
- [x] 2. Add `get_task_by_name` to protocol and TrinoClient
- [x] 2.1 Add `get_task_by_name` method to `DBClient` protocol in `database/types.py`
- Method signature: `def get_task_by_name(self, name: str) -> Task | None`
- Include docstring: "Fetch one published task by its short name (name__c)."
- _Requirements: 3.1_
- [x] 2.2 Implement `get_task_by_name` in `TrinoClient`
- SQL query selects same columns as `get_task_by_id` plus `t.name__c`
- WHERE clause filters on `t.name__c = ?` and `t.status__c = ?`
- ORDER BY `t.createddate DESC` to handle duplicates (return first match)
- Reuse same row-assembly logic as `get_task_by_id`
- Return None for empty string input or no matching rows
- _Requirements: 3.1, 3.2, 3.3_
- [ ]* 2.3 Write property test: Name lookup returns correct task
- **Property 3: Name lookup returns correct task**
- Generate random tasks with unique names, mock DB cursor
- Call `get_task_by_name(name)`, assert returned task's name matches
- **Validates: Requirements 3.2**
- [x] 3. Checkpoint
- Ensure all tests pass, ask the user if questions arise.
- [x] 4. Update MCP tool output formatting
- [x] 4.1 Add "Name" column to `list_open_tasks` output table
- Add "Name" to the header list (between task_id and Title)
- Add `str(task.name or "")` to each row
- Update the empty-row fallback to include the extra column
- _Requirements: 2.1_
- [x] 4.2 Add "Name" to `get_task_details` summary table
- Add "Name" to the header list
- Add `str(task.name or "")` to the row data
- _Requirements: 2.2_
- [ ]* 4.3 Write property test: Task output contains name
- **Property 2: Task output contains name**
- Generate random Task objects with non-None names using `st.text(min_size=1)`
- Format through output functions, assert name appears as substring
- **Validates: Requirements 2.1, 2.2**
- [x] 5. Update `get_task_details` resolution logic
- [x] 5.1 Add name-based fallback resolution in `get_task_details` tool
- After `get_task_by_id` returns None, call `db_client.get_task_by_name(task_id)`
- If both return None, return "Task not found or not published: {task_id}"
- _Requirements: 3.4, 3.5_
- [ ]* 5.2 Write property test: Name resolution equivalence
- **Property 4: Name resolution equivalence**
- Generate a task with non-None name, mock both lookup methods
- Call `get_task_details` with name and with ID, assert outputs identical
- **Validates: Requirements 3.4**
- [ ]* 5.3 Write property test: Unknown identifier returns not-found
- **Property 5: Unknown identifier returns not-found**
- Generate random strings not matching any known task ID or name
- Call `get_task_details`, assert output contains "not found"
- **Validates: Requirements 3.3, 3.5**
- [x] 6. Update FakeDBClient in tests and add `get_task_by_name` stub
- Add `name` field to `FakeTask` in existing test files
- Add `get_task_by_name` method to `FakeDBClient`
- Update existing test assertions that check table headers (add "Name" column)
- _Requirements: 3.1_
- [x] 7. Final checkpoint
- Ensure all tests pass, ask the user if questions arise.
## Notes
- Tasks marked with `*` are optional and can be skipped for faster MVP
- Each task references specific requirements for traceability
- Property tests use Hypothesis with `@settings(max_examples=100)`
- The implementation language is Python (matching the existing codebase)