Implementierungsplan für projekt-anlegen-Erweiterung hinzufügen
4 Tasks: rahmenbedingungen.md, CLAUDE.md-Verweis, Skill-Erweiterung (Extraktion + Schritt 3b Geokodierung + Review + CRM-Ablauf Firma→Kontakt→VC), Abschlussverifikation mit Geokodierungs-Regressionscheck. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
418
docs/superpowers/plans/2026-06-12-projekt-anlegen-erweiterung.md
Normal file
418
docs/superpowers/plans/2026-06-12-projekt-anlegen-erweiterung.md
Normal file
@@ -0,0 +1,418 @@
|
|||||||
|
# projekt-anlegen-Erweiterung Implementierungsplan
|
||||||
|
|
||||||
|
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
|
||||||
|
|
||||||
|
**Goal:** Den Skill `projekt-anlegen` um automatische Rahmenbedingungs-Bewertung, Extraktion von Käufer-Typ/Ansprechperson/Firma und den CRM-Schreibablauf Firma→Kontakt→Verkaufschance (mit Dedup und korrekter Verknüpfung) erweitern.
|
||||||
|
|
||||||
|
**Architecture:** Drei Dateien: neue Vorgaben-Datei `vorgaben/rahmenbedingungen.md` (Bewertungskriterien als Daten), `CLAUDE.md` (Verweis), und der Skill `.claude/skills/projekt-anlegen/SKILL.md` (Logik). Keine Testsuite — Verifikation über Datei-/Frontmatter-Prüfungen, eine Live-Geokodierung und CRM-Lesetests. CRM-Felder sind gegen die API verifiziert.
|
||||||
|
|
||||||
|
**Tech Stack:** Markdown-Skill, EspoCRM REST (curl, Key aus `.secrets/espocrm-api.md`), Nominatim-Geokodierung + Haversine (python3), Git (sha256).
|
||||||
|
|
||||||
|
**Referenz-Spec:** `docs/superpowers/specs/2026-06-12-projekt-anlegen-erweiterung-design.md`
|
||||||
|
|
||||||
|
Arbeitsverzeichnis für alle Befehle: `/home/tlg/mkt/bewerb`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Task 1: Vorgaben-Datei `vorgaben/rahmenbedingungen.md`
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Create: `vorgaben/rahmenbedingungen.md`
|
||||||
|
|
||||||
|
- [ ] **Step 1: Datei schreiben**
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
# Rahmenbedingungen (Misc-Bewertung)
|
||||||
|
|
||||||
|
Feste Kriterien für die automatische Bewertung von Rahmenbedingungen (Kategorie Misc)
|
||||||
|
durch den Skill `projekt-anlegen`. Ändern sich selten.
|
||||||
|
|
||||||
|
## Kriterien
|
||||||
|
|
||||||
|
| Dimension | Vorgabe | Bewertung |
|
||||||
|
|---|---|---|
|
||||||
|
| Verfügbar ab | sofort | Projektstart ≤ 8 Wochen ab heute → ✅; > 8 Wochen → ❌; nicht genannt → ❔ |
|
||||||
|
| Auslastung | 100 % (Vollzeit) | 75–100 % → ✅; < 75 % → ❔; nicht genannt → ❔ |
|
||||||
|
| Laufzeit | keine Einschränkung | jede Laufzeit → ✅ |
|
||||||
|
| Einsatzort / Remote | 100 % Remote oder Onsite ≤ 50 km um Sauerlach | siehe Einsatzort-Regel |
|
||||||
|
|
||||||
|
## Einsatzort-Regel
|
||||||
|
- 100 % Remote → ✅ (Ort egal).
|
||||||
|
- Onsite/Hybrid, Ort ≤ 50 km Luftlinie um Sauerlach → ✅.
|
||||||
|
- Onsite/Hybrid, Ort 50–60 km (grenzwertig) → ❔.
|
||||||
|
- Onsite/Hybrid, Ort > 60 km → ❌.
|
||||||
|
- Einsatzort/Remote-Anteil unklar oder nicht genannt → ❌.
|
||||||
|
|
||||||
|
Distanz = Luftlinie zwischen Onsite-Ort und Sauerlach (47,9721 N / 11,6528 O),
|
||||||
|
bestimmt per Geokodierung (Nominatim/OpenStreetMap, nur Deutschland) und Haversine.
|
||||||
|
Unbekannter/mehrdeutiger Ort → ❔.
|
||||||
|
|
||||||
|
Hinweis: „heute" = aktuelles Datum der Session. „8 Wochen" = 56 Tage.
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 2: Verifizieren**
|
||||||
|
|
||||||
|
Run: `grep -c "→" vorgaben/rahmenbedingungen.md`
|
||||||
|
Expected: ≥ 8 (alle Bewertungsregeln vorhanden).
|
||||||
|
|
||||||
|
- [ ] **Step 3: Commit**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git add vorgaben/rahmenbedingungen.md
|
||||||
|
git commit -m "Vorgaben-Datei rahmenbedingungen.md (Misc-Bewertungskriterien) anlegen"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Task 2: `CLAUDE.md` um Rahmenbedingungen ergänzen
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Modify: `CLAUDE.md`
|
||||||
|
|
||||||
|
- [ ] **Step 1: vorgaben-Zeile ergänzen**
|
||||||
|
|
||||||
|
Ersetze die Zeile:
|
||||||
|
|
||||||
|
```
|
||||||
|
- `vorgaben/` — feste Quellen: `Lebenslauf_Dr-Ing_Thomas_Langer.md`, `marketing.md`. Referenz; nicht ohne Auftrag ändern.
|
||||||
|
```
|
||||||
|
|
||||||
|
durch:
|
||||||
|
|
||||||
|
```
|
||||||
|
- `vorgaben/` — feste Quellen: `Lebenslauf_Dr-Ing_Thomas_Langer.md`, `marketing.md`, `rahmenbedingungen.md` (Misc-Kriterien für `projekt-anlegen`). Referenz; nicht ohne Auftrag ändern.
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 2: Verifizieren**
|
||||||
|
|
||||||
|
Run: `grep -c "rahmenbedingungen.md" CLAUDE.md`
|
||||||
|
Expected: `1`
|
||||||
|
|
||||||
|
- [ ] **Step 3: Commit**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git add CLAUDE.md
|
||||||
|
git commit -m "CLAUDE.md: rahmenbedingungen.md als Vorgabe ergänzen"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Task 3: Skill `projekt-anlegen` erweitern
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Modify: `.claude/skills/projekt-anlegen/SKILL.md`
|
||||||
|
|
||||||
|
Alle Edits sind exakte Ersetzungen am bestehenden Skill. Reihenfolge einhalten.
|
||||||
|
|
||||||
|
- [ ] **Step 1: Pipeline-Zeile und Voraussetzungen erweitern**
|
||||||
|
|
||||||
|
Ersetze:
|
||||||
|
|
||||||
|
```
|
||||||
|
Pipeline: Ausschreibung beschaffen → Anforderungen extrahieren → einordnen → gegen Lebenslauf bewerten → Matches berechnen → **Review im Chat (Pflicht)** → als Opportunity ins EspoCRM schreiben (per Shell) → verifizieren.
|
||||||
|
|
||||||
|
## Voraussetzungen und feste Pfade
|
||||||
|
|
||||||
|
| Zweck | Pfad |
|
||||||
|
|---|---|
|
||||||
|
| Lebenslauf (Markdown) | `vorgaben/Lebenslauf_Dr-Ing_Thomas_Langer.md` |
|
||||||
|
| EspoCRM-Zugangsdaten | `.secrets/espocrm-api.md` |
|
||||||
|
|
||||||
|
Beide Dateien mit dem Read-Tool lesen. Ohne Lebenslauf keine Bewertung, ohne Zugangsdaten kein CRM-Eintrag.
|
||||||
|
```
|
||||||
|
|
||||||
|
durch:
|
||||||
|
|
||||||
|
```
|
||||||
|
Pipeline: Ausschreibung beschaffen → Anforderungen + Käufer/Firma/Kontakt extrahieren → einordnen → gegen Lebenslauf bewerten → Rahmenbedingungen gegen Vorgaben bewerten → Matches berechnen → **Review im Chat (Pflicht)** → Firma → Kontakt → Verkaufschance ins EspoCRM schreiben (per Shell) → verifizieren.
|
||||||
|
|
||||||
|
## Voraussetzungen und feste Pfade
|
||||||
|
|
||||||
|
| Zweck | Pfad |
|
||||||
|
|---|---|
|
||||||
|
| Lebenslauf (Markdown) | `vorgaben/Lebenslauf_Dr-Ing_Thomas_Langer.md` |
|
||||||
|
| Rahmenbedingungen (Misc-Bewertung) | `vorgaben/rahmenbedingungen.md` |
|
||||||
|
| EspoCRM-Zugangsdaten | `.secrets/espocrm-api.md` |
|
||||||
|
|
||||||
|
Alle drei mit dem Read-Tool lesen. Ohne Lebenslauf keine fachliche Bewertung, ohne Rahmenbedingungen keine automatische Misc-Bewertung, ohne Zugangsdaten kein CRM-Eintrag.
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 2: Schritt 1 (Extraktion) erweitern**
|
||||||
|
|
||||||
|
Ersetze den Block ab `## Schritt 1: Ausschreibung beschaffen` bis einschließlich der Aufzählung mit `Rahmenbedingungen (Start, Einsatzort/Remote-Anteil, Auslastung, Laufzeit) zählen als Anforderungen der Kategorie Misc.` durch:
|
||||||
|
|
||||||
|
```
|
||||||
|
## Schritt 1: Ausschreibung beschaffen und Eckdaten extrahieren
|
||||||
|
|
||||||
|
Die Projektausschreibung kommt auf zwei Wegen:
|
||||||
|
- **URL:** Nennt der Nutzer eine URL, hole den Seitentext mit dem **WebFetch-Tool**.
|
||||||
|
- **Text:** Fügt der Nutzer den Ausschreibungstext direkt in den Chat ein, nutze diesen.
|
||||||
|
|
||||||
|
Bei Login-geschützten Portalen ist das Einfügen von Text nötig. Ist unklar, welche Quelle gilt, nachfragen.
|
||||||
|
|
||||||
|
Daraus entnehmen:
|
||||||
|
- **Projektname**: der Titel der Ausschreibung, ohne Portal-Zusatz (z. B. ohne „auf www.freelancermap.de").
|
||||||
|
- **Projekt-URL**: die tatsächliche URL der Seite, falls vorhanden.
|
||||||
|
- **Beschreibung und Aufgaben**: nur Kontext für die Bewertung, keine Tabellenzeilen.
|
||||||
|
- **Anforderungen**: jede Anforderung einzeln, im Originalwortlaut (behutsam kürzen ist erlaubt, Bedeutung nie verändern). Rahmenbedingungen (Start, Einsatzort/Remote-Anteil, Auslastung, Laufzeit) zählen als Anforderungen der Kategorie Misc.
|
||||||
|
- **Käufer-Typ**: Agentur (Wiederverkäufer) ODER Endkunde (Direktauftrag).
|
||||||
|
- Agentur-Signale: bekannte Personaldienstleister/Vermittler (Hays, GULP/Randstad, SThree/Computer Futures, Aristo …), Formulierungen wie „im Auftrag unseres Kunden", „für unseren Kunden", Vermittler-Kontext des Portals.
|
||||||
|
- Nicht eindeutig bestimmbar → im Review nachfragen.
|
||||||
|
- **Firmenname**: Name der Agentur bzw. des Endkunden.
|
||||||
|
- **Ansprechperson**: vollständiger Name, falls genannt.
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 3: Misc-Bullet in Schritt 3 auf Schritt 3b umlenken**
|
||||||
|
|
||||||
|
Ersetze:
|
||||||
|
|
||||||
|
```
|
||||||
|
- Rahmenbedingungen (Misc): Ortsbezug kann der Lebenslauf beantworten (Büro in Unterhaching bei München). Verfügbarkeit, Auslastung und Laufzeit kann er nicht beantworten → ❔ vorschlagen, Thomas setzt im Review ✅ oder ❌.
|
||||||
|
```
|
||||||
|
|
||||||
|
durch:
|
||||||
|
|
||||||
|
```
|
||||||
|
- Rahmenbedingungen (Misc) werden NICHT hier, sondern in Schritt 3b automatisch gegen `vorgaben/rahmenbedingungen.md` bewertet.
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 4: Neuen Schritt 3b vor Schritt 4 einfügen**
|
||||||
|
|
||||||
|
Füge direkt vor der Zeile `## Schritt 4: Match-Berechnung` ein:
|
||||||
|
|
||||||
|
````
|
||||||
|
## Schritt 3b: Rahmenbedingungen automatisch bewerten (Misc)
|
||||||
|
|
||||||
|
`vorgaben/rahmenbedingungen.md` lesen und jede Misc-Anforderung danach bewerten:
|
||||||
|
|
||||||
|
- **Verfügbar ab / Projektstart:** Start ≤ heute + 8 Wochen (56 Tage) → ✅; > 8 Wochen → ❌; nicht genannt → ❔. („heute" = aktuelles Datum.)
|
||||||
|
- **Auslastung:** 75–100 % → ✅; < 75 % → ❔; nicht genannt → ❔.
|
||||||
|
- **Laufzeit:** jede → ✅.
|
||||||
|
- **Einsatzort/Remote:** 100 % Remote → ✅. Bei < 100 % Remote (Hybrid/Onsite) Distanz des Onsite-Orts zu Sauerlach bestimmen (siehe unten): ≤ 50 km → ✅; 50–60 km → ❔; > 60 km → ❌. Einsatzort/Remote-Anteil unklar oder nicht genannt → ❌.
|
||||||
|
|
||||||
|
**Distanzbestimmung (Geokodierung, Pflicht bei Onsite/Hybrid):** Onsite-Ort per Nominatim auflösen und Luftlinie zu Sauerlach (47,9721 N / 11,6528 O) per Haversine rechnen. Bei Geokodierungs-Fehler/Mehrdeutigkeit (Ausgabe `UNBEKANNT`) → ❔. Nominatim-Policy: max. 1 Request/s.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python3 - "<ONSITE_ORT>" <<'PY'
|
||||||
|
import sys, json, urllib.request, urllib.parse
|
||||||
|
from math import radians, sin, cos, asin, sqrt
|
||||||
|
ort = sys.argv[1]
|
||||||
|
UA = 'bewerb-projekt-anlegen/1.0 (Thomas.Langer@destengs.com)'
|
||||||
|
def geo(q):
|
||||||
|
url = 'https://nominatim.openstreetmap.org/search?' + urllib.parse.urlencode(
|
||||||
|
{'q': q, 'format': 'json', 'countrycodes': 'de', 'limit': 1})
|
||||||
|
d = json.load(urllib.request.urlopen(
|
||||||
|
urllib.request.Request(url, headers={'User-Agent': UA}), timeout=20))
|
||||||
|
return (float(d[0]['lat']), float(d[0]['lon'])) if d else None
|
||||||
|
S = (47.9721357, 11.6528398)
|
||||||
|
g = geo(ort)
|
||||||
|
if not g:
|
||||||
|
print('UNBEKANNT')
|
||||||
|
else:
|
||||||
|
dlat = radians(g[0]-S[0]); dlon = radians(g[1]-S[1])
|
||||||
|
h = sin(dlat/2)**2 + cos(radians(S[0]))*cos(radians(g[0]))*sin(dlon/2)**2
|
||||||
|
print(f'{2*6371*asin(sqrt(h)):.1f} km')
|
||||||
|
PY
|
||||||
|
```
|
||||||
|
|
||||||
|
````
|
||||||
|
|
||||||
|
- [ ] **Step 5: Schritt 6 (Review) ersetzen**
|
||||||
|
|
||||||
|
Ersetze den gesamten Block von `## Schritt 6: Review im Chat — immer vor dem CRM-Eintrag` bis unmittelbar vor `## Schritt 7:` durch:
|
||||||
|
|
||||||
|
```
|
||||||
|
## Schritt 6: Review im Chat — immer vor jedem CRM-Schreibvorgang
|
||||||
|
|
||||||
|
Niemals ohne explizite Freigabe ins CRM schreiben. Im Chat zeigen:
|
||||||
|
|
||||||
|
1. Vorgesehener **Name** der Opportunity (= Projektname) und **Projektlink**.
|
||||||
|
2. **Käufer-Typ** (Agentur/Direktauftrag) mit Kurzbegründung.
|
||||||
|
3. **Firma:** Name und `type` (Reseller bei Agentur, Customer bei Direktauftrag); „neu anlegen" oder „bestehende nutzen: <Name/ID>". Bei ähnlichen, nicht identischen Treffern die Kandidatenliste zeigen und entscheiden lassen.
|
||||||
|
4. **Kontakt:** Name; „neu anlegen" oder „bestehend nutzen: <Name/ID>".
|
||||||
|
5. **Verknüpfungs-Zuordnung** der Verkaufschance (account vs. cAccount1, Kontakt).
|
||||||
|
6. Den wörtlichen Beschreibungs-Markdown in einem Codeblock.
|
||||||
|
7. Kurze Begründungen zu allen ❌- und ❔-Bewertungen (Must/Nice/Misc) außerhalb der Tabelle.
|
||||||
|
|
||||||
|
Korrekturen kommen als „Nr. X → ✅/❌/❔" oder als Korrektur zu Firma/Kontakt/Typ. Danach betroffene Matches neu berechnen und geänderte Werte nennen. Erst nach Freigabe weiter zu Schritt 7.
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 6: Schritt 7 (CRM-Eintrag) ersetzen**
|
||||||
|
|
||||||
|
Ersetze den gesamten Block von `## Schritt 7: CRM-Eintrag per Shell (curl)` bis unmittelbar vor `## Schritt 8:` durch:
|
||||||
|
|
||||||
|
````
|
||||||
|
## Schritt 7: CRM-Eintrag per Shell — Reihenfolge Firma → Kontakt → Verkaufschance
|
||||||
|
|
||||||
|
Diese Umgebung erreicht die CRM-Domain direkt; der Zugriff läuft per `curl`. Key/Base sicher laden (Werte nicht ausgeben):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
KEY=$(grep -oE '[0-9a-f]{32}' .secrets/espocrm-api.md | head -1)
|
||||||
|
BASE=$(grep -oE 'https://[^ `]+/api/v1' .secrets/espocrm-api.md | head -1)
|
||||||
|
```
|
||||||
|
|
||||||
|
### 7.1 Firma (Account)
|
||||||
|
Dedup-Suche mit einem Kern-Token des Namens (z. B. „Aristo"):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -s -G "$BASE/Account" -H "X-Api-Key: $KEY" \
|
||||||
|
--data-urlencode 'where[0][type]=contains' \
|
||||||
|
--data-urlencode 'where[0][attribute]=name' \
|
||||||
|
--data-urlencode 'where[0][value]=<KERN>' \
|
||||||
|
--data-urlencode 'select=name,type' --data-urlencode 'maxSize=50'
|
||||||
|
```
|
||||||
|
|
||||||
|
Treffer gemäß Review-Entscheidung behandeln. Bestehende Firma → deren `id` als ACCOUNT_ID merken. Sonst neu anlegen — Payload mit dem **Write-Tool** nach `/tmp/account.json` (`type`: `Reseller` bei Agentur, `Customer` bei Direktauftrag):
|
||||||
|
|
||||||
|
```json
|
||||||
|
{"name": "<FIRMENNAME>", "type": "<Reseller|Customer>"}
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -s -i -X POST "$BASE/Account" -H "X-Api-Key: $KEY" -H 'Content-Type: application/json' --data @/tmp/account.json
|
||||||
|
```
|
||||||
|
|
||||||
|
`id` aus der Antwort als ACCOUNT_ID merken.
|
||||||
|
|
||||||
|
### 7.2 Kontakt (Contact)
|
||||||
|
Dedup-Suche per Nachname:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -s -G "$BASE/Contact" -H "X-Api-Key: $KEY" \
|
||||||
|
--data-urlencode 'where[0][type]=contains' \
|
||||||
|
--data-urlencode 'where[0][attribute]=name' \
|
||||||
|
--data-urlencode 'where[0][value]=<NACHNAME>' \
|
||||||
|
--data-urlencode 'select=name,accountName' --data-urlencode 'maxSize=50'
|
||||||
|
```
|
||||||
|
|
||||||
|
Bestehenden Kontakt → dessen `id` als CONTACT_ID merken. Sonst neu anlegen — Payload nach `/tmp/contact.json`; die **Verknüpfung zur Firma über `accountId` ist beim Neu-Anlegen zwingend**:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{"firstName": "<VORNAME>", "lastName": "<NACHNAME>", "accountId": "<ACCOUNT_ID>"}
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -s -i -X POST "$BASE/Contact" -H "X-Api-Key: $KEY" -H 'Content-Type: application/json' --data @/tmp/contact.json
|
||||||
|
```
|
||||||
|
|
||||||
|
`id` aus der Antwort als CONTACT_ID merken. Namensaufteilung: letztes Token = `lastName`, der Rest = `firstName`; ungewöhnliche Fälle im Review klären.
|
||||||
|
|
||||||
|
### 7.3 Verkaufschance (Opportunity)
|
||||||
|
Duplikat-Prüfung des Namens (bei Treffer Suffix „ (2)", „ (3)", … anhängen):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -s -G "$BASE/Opportunity" -H "X-Api-Key: $KEY" \
|
||||||
|
--data-urlencode 'where[0][type]=startsWith' \
|
||||||
|
--data-urlencode 'where[0][attribute]=name' \
|
||||||
|
--data-urlencode 'where[0][value]=<PROJEKTNAME>' \
|
||||||
|
--data-urlencode 'select=name' --data-urlencode 'maxSize=100'
|
||||||
|
```
|
||||||
|
|
||||||
|
Payload nach `/tmp/opp.json`. Verknüpfungen je Käufer-Typ:
|
||||||
|
- **Agentur:** `cAccount1Id` = ACCOUNT_ID („Über Agentur"); **kein** `accountId`.
|
||||||
|
- **Direktauftrag:** `accountId` = ACCOUNT_ID; **kein** `cAccount1Id`.
|
||||||
|
|
||||||
|
```json
|
||||||
|
{"name": "<EINDEUTIGER_NAME>", "description": "<BESCHREIBUNG_MARKDOWN>", "cProjektlink": "<PROJEKT_URL>", "contactsIds": ["<CONTACT_ID>"], "cAccount1Id": "<ACCOUNT_ID — nur bei Agentur, sonst Feld weglassen und stattdessen accountId setzen>"}
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -s -i -X POST "$BASE/Opportunity" -H "X-Api-Key: $KEY" -H 'Content-Type: application/json' --data @/tmp/opp.json
|
||||||
|
```
|
||||||
|
|
||||||
|
Minimaler Payload genügt (Stage/Wahrscheinlichkeit setzt das CRM). Bei HTTP 400 steht die Ursache im `X-Status-Reason`-Header. Nachträgliche Korrekturen: `PUT "$BASE/Opportunity/<id>"`. Danach `/tmp/account.json /tmp/contact.json /tmp/opp.json` löschen.
|
||||||
|
````
|
||||||
|
|
||||||
|
- [ ] **Step 7: Schritt 8 (Verifizieren) um Verknüpfungen ergänzen**
|
||||||
|
|
||||||
|
Ersetze:
|
||||||
|
|
||||||
|
```
|
||||||
|
Prüfen: stimmen Name und Projektlink, hat die Beschreibung die Match-Zeile und alle Tabellenzeilen, sind ✅/❌/❔ intakt. Dann im Chat melden: gewählter Name (inkl. evtl. Duplikat-Suffix), beide Match-Zahlen und der Direktlink `https://crm.creature-go.com/#Opportunity/view/<id>`.
|
||||||
|
```
|
||||||
|
|
||||||
|
durch:
|
||||||
|
|
||||||
|
```
|
||||||
|
Prüfen: stimmen Name und Projektlink, hat die Beschreibung die Match-Zeile und alle Tabellenzeilen, sind ✅/❌/❔ intakt; sind die Verknüpfungen korrekt — Agentur: `cAccount1Name` gesetzt und `accountName` leer; Direktauftrag: `accountName` gesetzt; `contactsIds`/`contactId` enthält den Kontakt. Dann im Chat melden: gewählter Name (inkl. evtl. Duplikat-Suffix), beide Match-Zahlen, Firma und Kontakt (jeweils neu/bestehend) und der Direktlink `https://crm.creature-go.com/#Opportunity/view/<id>`.
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 8: Frontmatter- und Inhaltsprüfung**
|
||||||
|
|
||||||
|
Run:
|
||||||
|
```bash
|
||||||
|
head -2 .claude/skills/projekt-anlegen/SKILL.md
|
||||||
|
grep -c "Schritt 3b\|cAccount1Id\|rahmenbedingungen.md\|Nominatim\|contactsIds" .claude/skills/projekt-anlegen/SKILL.md
|
||||||
|
```
|
||||||
|
Expected: Zeile 2 = `name: projekt-anlegen`; Trefferzahl ≥ 5.
|
||||||
|
|
||||||
|
- [ ] **Step 9: Commit**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git add .claude/skills/projekt-anlegen/SKILL.md
|
||||||
|
git commit -m "Skill projekt-anlegen erweitern: Rahmenbedingungs-Bewertung, Käufer/Firma/Kontakt-Extraktion, CRM-Ablauf Firma→Kontakt→VC"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Task 4: Abschlussverifikation & Push
|
||||||
|
|
||||||
|
- [ ] **Step 1: Geokodierung live prüfen (Regressionscheck)**
|
||||||
|
|
||||||
|
Run:
|
||||||
|
```bash
|
||||||
|
python3 - "Augsburg" <<'PY'
|
||||||
|
import sys, json, urllib.request, urllib.parse
|
||||||
|
from math import radians, sin, cos, asin, sqrt
|
||||||
|
ort = sys.argv[1]
|
||||||
|
UA = 'bewerb-projekt-anlegen/1.0 (Thomas.Langer@destengs.com)'
|
||||||
|
def geo(q):
|
||||||
|
url = 'https://nominatim.openstreetmap.org/search?' + urllib.parse.urlencode(
|
||||||
|
{'q': q, 'format': 'json', 'countrycodes': 'de', 'limit': 1})
|
||||||
|
d = json.load(urllib.request.urlopen(
|
||||||
|
urllib.request.Request(url, headers={'User-Agent': UA}), timeout=20))
|
||||||
|
return (float(d[0]['lat']), float(d[0]['lon'])) if d else None
|
||||||
|
S = (47.9721357, 11.6528398)
|
||||||
|
g = geo(ort)
|
||||||
|
dlat = radians(g[0]-S[0]); dlon = radians(g[1]-S[1])
|
||||||
|
h = sin(dlat/2)**2 + cos(radians(S[0]))*cos(radians(g[0]))*sin(dlon/2)**2
|
||||||
|
print(f'{2*6371*asin(sqrt(h)):.1f} km')
|
||||||
|
PY
|
||||||
|
```
|
||||||
|
Expected: ca. `71 km` (→ Bewertung ❌, da > 60 km). Bestätigt, dass Geokodierung erreichbar ist.
|
||||||
|
|
||||||
|
- [ ] **Step 2: Struktur & Secrets**
|
||||||
|
|
||||||
|
Run:
|
||||||
|
```bash
|
||||||
|
test -f vorgaben/rahmenbedingungen.md \
|
||||||
|
&& grep -q "rahmenbedingungen.md" CLAUDE.md \
|
||||||
|
&& grep -q "Schritt 3b" .claude/skills/projekt-anlegen/SKILL.md \
|
||||||
|
&& grep -q "cAccount1Id" .claude/skills/projekt-anlegen/SKILL.md \
|
||||||
|
&& echo "ERWEITERUNG VOLLSTÄNDIG"
|
||||||
|
git check-ignore -q .secrets/espocrm-api.md && echo "secrets ignoriert" || echo "WARNUNG"
|
||||||
|
```
|
||||||
|
Expected: `ERWEITERUNG VOLLSTÄNDIG` und `secrets ignoriert`.
|
||||||
|
|
||||||
|
- [ ] **Step 3: Arbeitsbaum & Push**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git status --short
|
||||||
|
git push origin main
|
||||||
|
git rev-list --left-right --count HEAD...@{u}
|
||||||
|
```
|
||||||
|
Expected: sauberer Arbeitsbaum, Push erfolgreich, Zähler `0 0`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Selbstreview (gegen die Spec)
|
||||||
|
|
||||||
|
- **Spec §3 rahmenbedingungen.md** → Task 1 (identischer Inhalt). ✓
|
||||||
|
- **Spec §4 Extraktion (Käufer/Firma/Kontakt)** → Task 3 Step 2. ✓
|
||||||
|
- **Spec §5 Rahmenbedingungs-Bewertung + Geokodierung** → Task 3 Step 3+4 (Schritt 3b mit Nominatim/Haversine). ✓
|
||||||
|
- **Spec §6 CRM-Ablauf Firma→Kontakt→VC, Dedup, Verknüpfung, 6.2 accountId** → Task 3 Step 6 (7.1/7.2/7.3). ✓
|
||||||
|
- **Spec §7 Review-Erweiterung** → Task 3 Step 5. ✓
|
||||||
|
- **Spec §8 geänderte Dateien** → Tasks 1–3. ✓
|
||||||
|
- **Spec §2 verifizierte CRM-Fakten** (type=Reseller/Customer, account/cAccount1/contacts) → in Task 3 Steps 6/7 verwendet. ✓
|
||||||
|
|
||||||
|
Keine Platzhalter. Bezeichner konsistent: ACCOUNT_ID/CONTACT_ID, `cAccount1Id`, `accountId`, `contactsIds`, `type` ∈ {Reseller, Customer}.
|
||||||
Reference in New Issue
Block a user