AI Transformation with Claude · Part 4 · Week 12

Module 12. Capstone engagement and positioning

You have the skills. This module turns them into proof and a business: one real engagement, measured honestly and written up, and a plan to sell the next ten.

≈ 12 hreading 2 hlab 6 hdeliverables 4 h+ the engagement itself over the following weeks

By the end of this module you can

12.1 Choosing the capstone client

The capstone is your first public proof. Pick the client for what it lets you show, not for how exciting the technology is. A friendly client is fine. A client you cannot measure is not.

CriterionTest questionRed flag
Access to the ownerCan the owner give you 30 minutes a week and approve decisions the same week?Everything goes through someone who cannot decide
Measurable processCan you count something today (minutes, tickets, errors, response time) without new tools?"It should feel better" is the only goal
Data availableCan you read real examples (emails, orders, forms) legally and without a long IT project?Data sits in one person's head or on paper
Low riskIf Claude is wrong, is the output a draft that a person checks, not a payment or a legal notice?The first idea is to auto-send to customers

Your three friendly candidates, as hypotheses to check in a first call. None of this is known yet; it is what you need to find out.

CandidateLikely process to improveLikely measureWatch out for
Bat Yam garage (fuel injector cleaning)Enquiries by phone and message in Hebrew and Russian; quote and booking follow-up; explaining test results to customersTime to reply to an enquiry, enquiries that become bookingsLow volume: a small sample can show nothing. Enquiries may not be logged anywhere
Ceramics studioReplying to enquiries, workshop sign-ups, product texts in several languagesAdmin hours per week, time to replySeasonal sales; very small data; hard to prove business impact
Small logistics firmShipment-status emails, customer updates, exception handlingTime to first reply, emails per clerk per day, errors found in reviewCustomer data privacy; a busy owner. Best match to your target niche
Rule of thumb: if the logistics firm passes all four tests, choose it, because its case study is the one your future clients will recognise. If it fails on access or data, use the garage or the studio and be open that the sample is small.

12.2 The engagement plan, week by week

Plan eight weeks. The pilot itself is two weeks; the rest is what makes it a proper engagement. Collect the baseline during the audit, before the pilot starts, using the same method you will use later.

eight-week capstone; baseline is collected in weeks 1 and 2 Audit + baseline Architect ADR Pilot build two weeks, human review on Handover pack + training Measurement same method as baseline W1W2W3W4W5W6W7W8 ADR signed pilot ends handover done results review
Gates are decisions, not dates: the owner signs the ADR before you build, and approves the numbers before you publish.
WeekPhaseWhat you doOutputGate
Before W1AgreementOne-page scope: goal, metric, data you may use, who approves, what may be published, end dateSigned scope (use the Module 11 proposal template)Owner signs
W1AuditShadow the process, collect 20 to 50 real examples, start logging the baseline, check data and privacy termsProcess map, baseline log started, risk listMetric and method agreed
W2ArchitectureCompare options, pick one, build a small test set with correct answers, set the pass markADR, test set, pilot planOwner signs the ADR
W3 to W4Pilot build and runBuild the smallest working version, test against the test set, run with real work and a person reviewing every outputWorking pilot, daily log of correctionsPass mark met before real use; stop rule agreed
W5HandoverWrite the pack, record the training video, train two people, agree support termsHandover packClient staff run it without you once
W6 to W8MeasurementKeep logging with the same method, review errors weekly, compare with baselineResults sheet with n, period, caveatsOwner approves numbers for publication

After W8, write the case study (12.6) and decide with the owner whether to move to a retainer.

12.3 The architecture decision record

An ADR is a one-to-two page note that records one decision, the options you rejected and why. It protects the client (they know what they bought), you (you can show the reasoning) and the next engineer. Write one per major choice. For the pilot you usually need only one or two.

# ADR-001: [Decision title, e.g. "Draft shipment-status replies with Claude via API, human sends"]

Status: Proposed | Accepted | Superseded by ADR-00X
Date: YYYY-MM-DD
Deciders: [client owner], [your name, Vastama]

## Context
Process, current steps, volumes per week, languages, systems involved,
data sensitivity, constraints (budget, staff, deadline).

## Decision drivers
- [e.g. replies checked by a person before sending]
- [e.g. Hebrew, Russian and English]
- [e.g. no customer data stored outside the client's accounts]

## Options considered
| Option | Pros | Cons |
|---|---|---|
| 0. Do nothing | No risk, no cost | Problem stays |
| 1. Claude app with a Project, no code | Fast, no build | Manual copy and paste, no logging |
| 2. API plus small Python service, human review | Logging, control | Needs hosting and maintenance |
| 3. Agent with MCP into the client's system | Least manual work | Highest risk and effort |

## Decision
We will [option], because [two or three reasons tied to the drivers].

## Consequences
- Good: ...
- Bad or costly: ...
- Risks and mitigations: ...

## Data and privacy
What data goes to Claude, plan and terms checked on [date] (link),
retention setting, who has access, what is never sent.

## Model choice and cost tracking
Model tier and why (see docs.claude.com for current models and pricing).
How cost per task will be logged and who reviews it monthly.

## Evaluation
Test set size and source, pass mark, who judges, when it is re-run.

## Human oversight and rollback
Who approves outputs, what happens when Claude is wrong,
how to switch the system off in one step.

## Review date
[date to revisit this decision]
Keep the options table honest. Always include "do nothing" and "Claude app with a Project, no code" as options. If the no-code option is good enough, say so: it is a better engagement than a custom build the client does not need.

12.4 The handover pack

A pilot that only you can run is a dependency, not a result. The pack lets the client run, check and switch off the system without calling you.

ItemContentsTest it with
RunbookWhat the system does, daily steps, how to review outputs, what to do when Claude is wrong, how to switch off and roll back, where logs liveA clerk follows it with you silent
Admin guideAccounts and who owns them, API key handling and rotation, spend limit, user access, where prompts and config are stored, how to change a prompt safelyThe owner or IT contact adds and removes a user
Training video10 minutes or less, screen recording of one real task from start to finish, in the language the staff work in. Use captions. Hide customer dataA new employee watches it and completes a task
Support termsWhat is covered (bugs, prompt fixes), what is not (new features), response times in working days, how to report a problem, how to end support, who owns the code and promptsThe owner reads it and can say it back in one sentence
Source and configRepository or folder with code, prompts, test set and the ADR; no secrets insideAnother developer can run the tests

12.5 Measuring results honestly

Your credibility depends on numbers that survive a sceptical question. Use one primary metric and up to two supporting ones. Measure the baseline and the after period the same way, written down before the pilot.

MetricBaselineAftern and periodSource
[primary metric, with unit][value][value][n baseline / n after, dates][mail log, CRM export, stopwatch sample]
[supporting metric][value][value][n, dates][source]
[quality metric: errors found in review][value][value][n, dates][source]

Caveats you should check against every pilot, and state the ones that apply:

Write "in this pilot, at this company, over these weeks" next to every figure. Never turn one pilot into a rule such as "Claude cuts reply time in half".

12.6 Writing the case study

One page, one client, plain language. Five parts:

  1. Problem: what was slow, costly or error-prone, in the owner's own terms
  2. Approach: audit, decision, pilot, handover, with the dates
  3. Solution: what was built, which Claude surface, where a person reviews
  4. Results: the baseline-versus-after table, n, period, and the caveats
  5. Quote: one or two sentences from the client, in their words

Rules for approval, kept in writing:

12.7 Positioning: niche and statement

"AI for everyone" sells to no one. A niche lets you reuse your work, speak the client's language and be the obvious name for one kind of problem.

Niche optionWhy it can work for youCheck before you commit
Israeli logistics and distribution SMBsHeavy email and document load, multi-language customers, clear time metrics, repeatable pilotsCan you reach 30 such companies in 90 days?
Service businesses (garages, studios, clinics, agencies)Many small owners, quick decisions, enquiry and booking workIs the volume large enough to measure?
Hebrew and Russian-speaking businessesYou work natively in both; fewer consultants do; clear language advantageIs it a market or only a language? Combine it with an industry

Pick one primary niche and one secondary. Your pipeline does not have to be exclusive; your message does.

The positioning formula

I help [type of company, size, place]
[achieve a specific outcome]
by [your method, in plain words],
so that [measurable result they can check].
Unlike [the usual alternative], I [one real difference].

Draft for you to edit, not a claim of past results:

I help Israeli logistics and distribution companies with 10 to 100 staff
cut manual email and document work
with Claude, in Hebrew, Russian and English, using a two-week pilot,
so that the time saved is measured against a baseline before they decide to scale.
Unlike a general AI course or a tool reseller, I audit first, build the pilot myself,
and hand over a pack your team can run.

Test it: read it to someone in the niche. If they cannot repeat what you do, shorten it.

12.8 The service menu

A ladder lets a client start small and climb as trust grows. Each step has a clear output and an end.

Workshop half day, whole team Audit find 2 to 3 use cases and a baseline Pilot two-week build, measured, handed over Retainer monitor, fix, extend, review each month more trust, more commitment, more value →
Every step ends with a decision for the client: stop here, or go one step up.
OfferWhat the client getsTypical lengthEntry point for
Workshop: "Claude for your team"Hands-on half day, team's own tasks, a prompt starter packHalf a dayOwners who want to see it before they commit
AI readiness auditProcess review, 2 to 3 ranked use cases, baseline, risk and data notes, a pilot proposal1 to 2 weeksClients who know they have a problem but not which one
PilotOne use case built, run for two weeks with review, measured, handover packUnder 8 weeks including measurementClients with a chosen process and data
RetainerMonthly monitoring, fixes, prompt and model updates, one improvement per month, short reportRolling, with noticeClients with a pilot that worked

Set your fees in the proposal template from Module 11. This course gives no price list, because the right figure depends on your costs and the client's scope.

It maps to Vastama's pipeline: the audit is design, the pilot is build, the measurement is test, the retainer is monitor. Say that on the website; it makes the AI service feel like part of what Vastama already does.

12.9 The Vastama AI service page

One page, in English and Hebrew (and Russian if you target it), on your existing GitHub Pages site. Suggested structure:

  1. Headline and positioning statement (12.7), with one button: "Book a 20-minute call"
  2. Who it is for: your niche, in three lines
  3. What we do: the four-step service menu
  4. How an engagement runs: audit, architecture, pilot, handover, measurement, using the timeline from 12.2
  5. Case study: the capstone, with before/after table. Until it is approved, show "Case study coming" and nothing invented
  6. Proof: certificates you have earned, public MCP servers, open-source repositories, talks
  7. Workshop: agenda and how to book
  8. Questions owners ask: data privacy (link to the Anthropic Trust Center pages with the date you checked), languages, what happens if Claude is wrong, who owns the work
  9. Contact: email, LinkedIn link, company details

Technical points: separate files per language (for example /ai/ and /he/ai/), lang and dir="rtl" on the Hebrew page, hreflang links between versions, a readable title and description for search, and no testimonials or logos you do not have approval for.

12.10 LinkedIn profile and content plan

Your profile at linkedin.com/in/sheroleg is the page a prospect checks after your message. Make it say the same thing as your website.

Two posts a week for eight weeks. Each post should teach one thing, show one thing you built, or answer one question an owner asks. Do not name a client without written approval.

WeekPost APost B
1Who I help and why this nicheWhat a two-week pilot looks like (the timeline)
2Three office tasks Claude handles well, with a synthetic exampleOne email in Hebrew, one in Russian, one in English: triage demo on made-up data
3The four tests I use before taking on any pilotData privacy: the questions owners ask and the sources I show them
4An ADR explained: how I record a design decision (template, no client data)Why every output has a human reviewer at first
5A public repo or MCP server I built, and what it doesHow I measure a pilot honestly: baseline, n, caveats
6A mistake or surprise from the build (only what the client allows)Cost tracking: how I watch spend per task
7The case study, with approved numbers and quoteWhat I would do differently next time
8The half-day workshop: agenda and who it is forAnswers to the five questions owners asked me this month

12.11 The outreach plan

The 30-company list

Build a list of 30 target companies in your primary niche: 10 reached through a warm introduction, 10 through a shared connection or meetup, 10 researched cold. For each, record the reason it might need help (a trigger such as hiring a customer service clerk, growth in shipments, a new branch) and the person who decides. The tracker in the lab holds the list.

Warm introductions first

Ask people who already trust you (former colleagues, suppliers, the capstone client) for introductions. Make it easy: send a two-line blurb they can forward.

Hi [name], I'm offering a free 20-minute call to Israeli logistics owners about
cutting email and document work with Claude. If anyone comes to mind,
could you forward this to them? Happy to write the intro for you.

Follow-up cadence

DayAction
0First message, short, one specific reason, one clear ask
4Follow-up that adds something useful (a one-page example from their sector), not "just checking in"
10Different channel if you have a legitimate one (LinkedIn note, phone call to a published business number)
24Last note: you will not write again unless they ask; leave the door open
AfterMark as paused for 90 days. Do not keep chasing
Israeli law restricts unsolicited advertising messages sent in bulk (see the spam provision of the Communications Law). Ask a lawyer before you email lists. Warm introductions, personal LinkedIn notes and phone calls to a business are the safer starting channels.

A short outreach message

Hebrew (replace the bracketed parts with something true and specific about the company):

שלום [שם],

קוראים לי אולג, אני מנהל את Vastama, חברת שירותי IT. אנחנו עוזרים לחברות קטנות
בתחום הלוגיסטיקה לחסוך עבודה ידנית במיילים ובמסמכים בעזרת Claude, בעברית,
ברוסית ובאנגלית.

ראיתי ש[פרט אמיתי על החברה]. אשמח לשיחה של 20 דקות ולהראות דוגמה אחת
מהתחום שלכם, בלי התחייבות.

מתאים לך [יום] בשעה [שעה]?

אולג
Vastama | [טלפון]

English:

Hi [name],

I'm Oleg, I run Vastama, an IT services company. We help small logistics
companies cut manual email and document work with Claude, in Hebrew,
Russian and English.

I saw that [a real detail about the company]. I'd like to talk for 20 minutes
and show one example from your sector. No commitment.

Would [day] at [time] work?

Oleg
Vastama | [phone]

12.12 The workshop offer: "Claude for your team"

A half-day workshop is your lowest-risk first sale and your best source of audit leads. Run it on the client's real tasks, with their data rules.

TimeBlockOutcome
0:00 to 0:30What Claude is and is not; data and privacy rules for this companyShared rules the team can follow
0:30 to 1:30Hands-on: each person brings two real tasks; prompts with context and examplesEach person has one working prompt
1:30 to 1:45Break
1:45 to 2:45Projects, files and connectors for the team's work; checking Claude's answersA shared Project set up for one team task
2:45 to 3:30Find use cases worth a pilot; rank by the six-point screen from Module 1A shortlist of 2 to 3 candidates
3:30 to 4:00Next steps, questionsA written follow-up plan within two days

Pre-work: a 15-minute call with the owner, accounts ready, a list of sample tasks with sensitive details removed. After the workshop, send a one-page summary and the audit offer. Run the first one for a friendly client and ask for feedback you may quote only after approval.

12.13 Building credibility

Credibility is evidence a stranger can check in two minutes. List only what you have actually done.

SourceHow to use it
Anthropic Academy certificatesFinish the courses relevant to your offer on anthropic.skilljar.com; add earned certificates to LinkedIn and the website with the date
Public MCP serversYour servers for Israeli and EU public data are a strong demonstration. Give each a clear README, an example, and a short post
Open-source repositoriesThe prompt library (Module 2), project template (Module 7) and your lab code, with no client data or keys. Pin the best three
Talks at local meetupsOffer a 20-minute talk built on the capstone, with approved numbers. Record it if allowed and link it from the website
Published case studyThe strongest item. Everything else supports it
Build Publish Outreach Deliver Case study each turn gives you something new to show
The 90-day loop: build something public, publish it, reach out with it, deliver, and turn the delivery into the next thing to publish.

Lab 12. Plan, case study generator, go-to-market tracker

Goal: three working tools you will use for the next 90 days. Everything runs locally; only the case study script calls the API, and it has a dry-run mode that does not.

Part 1 (about 2 h). Engagement plan and ADR for your chosen client

  1. Score your three candidates against the four tests in 12.1. Choose one and write two lines on why.
  2. Copy the table from 12.2 into a document and fill in real dates, the named owner contact and the primary metric.
  3. Copy the ADR template above into adr/ADR-001.md and fill it in after the audit. Include "do nothing" and "Claude app with a Project" as options.
  4. Write the one-page scope with the owner: goal, metric, data, who approves, what may be published.

Part 2 (about 2 h). Case study draft generator

Setup (same repository as the earlier labs):

cd claude-transformation
source .venv/bin/activate
pip install anthropic python-dotenv
mkdir module12 && cd module12

engagement.json (all values are placeholders; fill them from your real logs)

{
  "client_label": "A small logistics company in central Israel",
  "client_name": "[client name]",
  "client_name_approved": false,
  "sector": "logistics",
  "problem": "[What was slow or error-prone, in the owner's words. Facts only.]",
  "approach": "[Audit, architecture decision, two-week pilot. Add real dates.]",
  "solution": "[What was built: model, tools, the human review step.]",
  "metrics": [
    {
      "name": "Median time to first reply to a shipment-status email",
      "unit": "minutes",
      "better_when": "lower",
      "baseline": null,
      "after": null,
      "n_baseline": null,
      "n_after": null,
      "period": "[baseline dates] vs [pilot dates]",
      "method": "[how it was measured, by whom]"
    },
    {
      "name": "Status emails handled per clerk per day",
      "unit": "emails",
      "better_when": "higher",
      "baseline": null,
      "after": null,
      "n_baseline": null,
      "n_after": null,
      "period": "[baseline dates] vs [pilot dates]",
      "method": "[how it was measured, by whom]"
    }
  ],
  "caveats": [
    "[confirm] One company, one pilot, short period.",
    "[confirm] No control group; other changes in the period are possible.",
    "[confirm] Time figures come from the mail log, not from self-reports."
  ],
  "quote": {
    "text": null,
    "speaker": null,
    "role": null,
    "approved": false,
    "approval_date": null
  }
}

case_study.py

#!/usr/bin/env python3
"""case_study.py - turn engagement.json into draft case studies (English + Hebrew).

The output is a DRAFT for you to edit. Facts come only from the JSON.
Percent changes are computed here in Python, not by the model.

Usage:
    python case_study.py engagement.json --dry-run --allow-missing   # no API call
    python case_study.py engagement.json --out drafts                # real run
"""
import argparse
import copy
import json
import sys
from pathlib import Path

MODEL = "claude-sonnet-5-5"  # check current IDs at docs.claude.com

REQUIRED_TOP = ["client_label", "problem", "approach", "solution", "metrics", "caveats"]
REQUIRED_METRIC = ["name", "unit", "baseline", "after", "n_baseline", "n_after", "period"]

SYSTEM = """You draft case studies for a small IT consultancy.
Rules:
- Use only facts and numbers that appear in the JSON. Never invent numbers, names, dates or quotes.
- If a value is null, empty or a [bracketed placeholder], keep it visible as [MISSING: field name].
- Use change_pct exactly as given. Do not recalculate. If it is null, say the change cannot be stated yet.
- Next to every result, state the sample sizes (n_baseline, n_after) and the period.
- If quote.approved is not true, write [QUOTE PENDING APPROVAL] and no quote text.
- If client_name is absent, use client_label. Do not guess the client's identity.
- Include every caveat from the JSON in a Caveats section.
- Plain, direct language. No hype, no superlatives.
- Sections, in order: Problem, Approach, Solution, Results, Caveats, Quote.
- Output Markdown only."""

LANG_NOTES = {
    "en": "Write in English.",
    "he": (
        "Write in natural business Hebrew, not a literal translation. "
        "Translate the section headings into Hebrew. Keep numbers, units and "
        "product names (Claude, MCP, Python) in Latin characters."
    ),
}


def pct_change(baseline, after):
    """Percent change from baseline to after, or None if it cannot be computed."""
    nums = (int, float)
    if not isinstance(baseline, nums) or not isinstance(after, nums) or baseline == 0:
        return None
    return round((after - baseline) / baseline * 100, 1)


def find_missing(data):
    """Return a list of required fields that are empty."""
    missing = []
    for key in REQUIRED_TOP:
        if data.get(key) in (None, "", []):
            missing.append(key)
    for i, metric in enumerate(data.get("metrics") or []):
        for key in REQUIRED_METRIC:
            if metric.get(key) is None:
                missing.append(f"metrics[{i}].{key}")
    return missing


def prepare(data):
    """Copy the data, add computed changes, strip anything not approved."""
    d = copy.deepcopy(data)
    for metric in d.get("metrics", []):
        metric["change_pct"] = pct_change(metric.get("baseline"), metric.get("after"))
    quote = d.get("quote") or {}
    if quote.get("approved") is not True:
        d["quote"] = {"approved": False}  # quote text never reaches the model
    if d.get("client_name_approved") is not True:
        d.pop("client_name", None)
    return d


def build_prompt(prepared, lang):
    return (
        f"{LANG_NOTES[lang]}\n\n"
        "Draft the case study from this engagement data:\n\n"
        + json.dumps(prepared, ensure_ascii=False, indent=2)
    )


def draft(client, prepared, lang):
    msg = client.messages.create(
        model=MODEL,
        max_tokens=2000,
        system=SYSTEM,
        messages=[{"role": "user", "content": build_prompt(prepared, lang)}],
    )
    return "".join(b.text for b in msg.content if b.type == "text")


def main():
    ap = argparse.ArgumentParser(description=__doc__.splitlines()[0])
    ap.add_argument("path", help="engagement JSON file")
    ap.add_argument("--langs", default="en,he", help="comma list: en,he")
    ap.add_argument("--out", default=".", help="output folder")
    ap.add_argument("--dry-run", action="store_true", help="print prompts, no API call")
    ap.add_argument("--allow-missing", action="store_true", help="continue with gaps")
    args = ap.parse_args()

    data = json.loads(Path(args.path).read_text(encoding="utf-8"))
    missing = find_missing(data)
    if missing:
        print("Missing fields:", ", ".join(missing), file=sys.stderr)
        if not args.allow_missing:
            print("Fill them in, or pass --allow-missing for a gap-marked draft.", file=sys.stderr)
            return 2

    prepared = prepare(data)
    langs = [x.strip() for x in args.langs.split(",") if x.strip()]
    bad = [x for x in langs if x not in LANG_NOTES]
    if bad:
        print("Unsupported language:", ", ".join(bad), file=sys.stderr)
        return 2

    if args.dry_run:
        for lang in langs:
            print(f"--- PROMPT ({lang}) ---")
            print(build_prompt(prepared, lang))
        return 0

    import anthropic  # imported here so --dry-run works without the package
    from dotenv import load_dotenv

    load_dotenv()
    client = anthropic.Anthropic()
    out_dir = Path(args.out)
    out_dir.mkdir(parents=True, exist_ok=True)
    for lang in langs:
        text = draft(client, prepared, lang)
        header = (
            "<!-- DRAFT for Oleg to edit. Not approved by the client. "
            "Check every number against the source data. -->\n\n"
        )
        target = out_dir / f"case_study_{lang}.md"
        target.write_text(header + text + "\n", encoding="utf-8")
        print("Wrote", target)
    return 0


if __name__ == "__main__":
    sys.exit(main())

Run it:

python case_study.py engagement.json --dry-run --allow-missing   # see the prompts, no API call
python case_study.py engagement.json                             # stops and lists missing fields
python case_study.py engagement.json --out drafts                # after you fill in real data

What to notice: the percent change is calculated in Python, the quote text never reaches the model unless approved is true, and missing values stay visible as [MISSING: ...]. Edit both drafts by hand. The model's Hebrew is a first draft; your version is the one you publish.

Part 3 (about 2 h). 90-day go-to-market tracker

gtm_tracker.csv (sample rows with placeholder names; replace them with your 30 companies)

company,segment,source,contact_role,stage,status,first_contact,last_contact,next_action,next_action_date,notes
[Company 01],logistics,warm_intro,owner,contacted,open,2026-10-19,2026-10-19,follow-up message,2026-10-23,[who introduced]
[Company 02],logistics,linkedin,operations manager,replied,open,2026-10-19,2026-10-21,book 20-min call,2026-10-26,
[Company 03],services,cold,owner,target,open,,,first message,2026-10-22,
[Company 04],garage,warm_intro,owner,call_held,open,2026-10-12,2026-10-20,send audit scope,2026-10-27,[friendly capstone candidate]
[Company 05],ceramics,warm_intro,owner,contacted,lost,2026-10-12,2026-10-14,,,[reason in one line]

gtm_summary.py

#!/usr/bin/env python3
"""gtm_summary.py - weekly summary of a 90-day go-to-market tracker CSV.

Usage:
    python gtm_summary.py gtm_tracker.csv
    python gtm_summary.py gtm_tracker.csv --today 2026-10-26
Weeks run Sunday to Saturday (Israeli work week).
"""
import argparse
import csv
import sys
from collections import Counter
from datetime import date, datetime, timedelta
from pathlib import Path

STAGES = [
    "target",
    "contacted",
    "replied",
    "call_held",
    "audit_proposed",
    "audit_won",
    "pilot_won",
    "retainer_won",
]
STATUSES = {"open", "lost", "paused"}
STALE_DAYS = 21


def parse_date(text, row_no, column):
    text = (text or "").strip()
    if not text:
        return None
    try:
        return datetime.strptime(text, "%Y-%m-%d").date()
    except ValueError:
        raise SystemExit(f"Row {row_no}: {column} '{text}' is not YYYY-MM-DD")


def load(path):
    rows = []
    with open(path, newline="", encoding="utf-8") as f:
        for n, row in enumerate(csv.DictReader(f), start=2):  # row 1 is the header
            stage = (row.get("stage") or "").strip()
            status = (row.get("status") or "open").strip()
            if stage not in STAGES:
                raise SystemExit(f"Row {n}: unknown stage '{stage}'. Use one of: {', '.join(STAGES)}")
            if status not in STATUSES:
                raise SystemExit(f"Row {n}: unknown status '{status}'. Use one of: {', '.join(sorted(STATUSES))}")
            row["stage"], row["status"] = stage, status
            for col in ("first_contact", "last_contact", "next_action_date"):
                row[col] = parse_date(row.get(col), n, col)
            rows.append(row)
    return rows


def week_start(day):
    """Sunday of the week containing `day`."""
    return day - timedelta(days=(day.weekday() + 1) % 7)


def reached(row, stage):
    return STAGES.index(row["stage"]) >= STAGES.index(stage)


def rate(part, whole):
    return f"{part / whole * 100:.0f}%" if whole else "n/a"


def summarise(rows, today):
    start = week_start(today)
    end = start + timedelta(days=6)
    out = []
    out.append(f"GTM summary for week {start} to {end} (today {today})")
    out.append(f"Rows: {len(rows)}  open: {sum(r['status'] == 'open' for r in rows)}")

    out.append("\nFunnel (companies that reached each stage)")
    previous = None
    for stage in STAGES:
        count = sum(reached(r, stage) for r in rows)
        step = f"  ({rate(count, previous)} of previous)" if previous is not None else ""
        out.append(f"  {stage:<15}{count:>4}{step}")
        previous = count

    new_this_week = [r for r in rows if r["first_contact"] and start <= r["first_contact"] <= end]
    touched = [r for r in rows if r["last_contact"] and start <= r["last_contact"] <= end]
    out.append("\nThis week")
    out.append(f"  new first contacts: {len(new_this_week)}")
    out.append(f"  companies touched:  {len(touched)}")

    out.append("\nReply rate by source (replied or further / contacted or further)")
    contacted_by, replied_by = Counter(), Counter()
    for r in rows:
        if reached(r, "contacted"):
            contacted_by[r["source"]] += 1
            if reached(r, "replied"):
                replied_by[r["source"]] += 1
    for source in sorted(contacted_by):
        c, p = contacted_by[source], replied_by[source]
        out.append(f"  {source:<12}{p}/{c}  {rate(p, c)}")
    if not contacted_by:
        out.append("  no contacts yet")

    open_rows = [r for r in rows if r["status"] == "open"]
    due = sorted(
        (r for r in open_rows if r["next_action_date"] and r["next_action_date"] <= end),
        key=lambda r: r["next_action_date"],
    )
    out.append("\nActions due by end of this week")
    for r in due:
        flag = "OVERDUE " if r["next_action_date"] < today else ""
        out.append(f"  {r['next_action_date']}  {flag}{r['company']}: {r['next_action']}")
    if not due:
        out.append("  none")

    stale = [
        r for r in open_rows
        if r["stage"] == "contacted" and r["last_contact"]
        and (today - r["last_contact"]).days > STALE_DAYS
    ]
    out.append(f"\nStale: contacted, no reply, last touch over {STALE_DAYS} days ago")
    for r in stale:
        out.append(f"  {r['company']} (last touch {r['last_contact']}): pause or close")
    if not stale:
        out.append("  none")

    no_action = [r for r in open_rows if not r["next_action_date"] and r["stage"] != "retainer_won"]
    out.append("\nOpen rows with no next action date")
    for r in no_action:
        out.append(f"  {r['company']}")
    if not no_action:
        out.append("  none")
    return "\n".join(out)


def main():
    ap = argparse.ArgumentParser(description=__doc__.splitlines()[0])
    ap.add_argument("csv_path")
    ap.add_argument("--today", help="override today, YYYY-MM-DD")
    args = ap.parse_args()
    if not Path(args.csv_path).exists():
        print(f"File not found: {args.csv_path}", file=sys.stderr)
        return 2
    today = datetime.strptime(args.today, "%Y-%m-%d").date() if args.today else date.today()
    print(summarise(load(args.csv_path), today))
    return 0


if __name__ == "__main__":
    sys.exit(main())

Run it every Sunday morning:

python gtm_summary.py gtm_tracker.csv
python gtm_summary.py gtm_tracker.csv --today 2026-10-22   # test with a chosen date
  1. Replace the sample rows with your 30 targets (stage target, a first action and date for each).
  2. Update the CSV after every contact. Stages are in order: target, contacted, replied, call_held, audit_proposed, audit_won, pilot_won, retainer_won. A lost or paused company keeps its furthest stage and gets a different status.
  3. After four weeks, read the reply rate by source and decide where to put your time.

Deliverables

Six items. The first needs the engagement to finish; the others you can complete in this module.

  1. Published case study with before/after numbers. English and Hebrew. Client-approved numbers, quote and naming. Pass check: every figure traces to a log, and n, period and caveats appear next to it.
  2. 5-minute demo video script (below). Pass check: a stranger understands the problem and the result without a call.
  3. Positioning statement. One sentence from the formula in 12.7. Pass check: someone in the niche can repeat it.
  4. Service menu. The four-step ladder with outputs and lengths for your own offer, fees left to your proposal template.
  5. Website page outline. The nine sections in 12.9 with your real text for each, and "coming" where the case study is not yet approved.
  6. 30-company outreach list template. The gtm_tracker.csv from the lab, filled with 30 real targets, each with a trigger and a named role. Add columns website, size_estimate and trigger if you need them; the script ignores extra columns.

5-minute demo video script

TimeOn screenYou say
0:00 to 0:30Title and your face or logoWho you are, who you help, the problem in the client's words (if approved)
0:30 to 1:15The old process, screen recording with data hiddenWhat the work looked like before, and what was counted
1:15 to 3:00The pilot handling one real example from start to finish, including the human reviewWhat Claude does, what the person checks, what happens when it is wrong
3:00 to 4:00The before/after table, with n, period and caveats on screenThe numbers as written in the case study, nothing added
4:00 to 4:40The handover packHow the client runs, checks and stops it without you
4:40 to 5:00Contact slideThe next step: a 20-minute call or a readiness audit

Record in the language of your audience, add captions in the other language, and show no customer data you do not own.

Self-check

  1. The owner of your capstone client says "just try it" but nobody has ever measured how long the task takes. What do you do?
    Model answerCreate the baseline yourself in the audit: log the same measure for one to two weeks before the pilot, with a method both of you agree. If that is impossible, choose a different process. If you go ahead anyway, report the result as qualitative and say so.
  2. The owner sends "great job, really helped!" on WhatsApp. Can it go on the website as a quote?
    Model answerNot as it stands. Send the exact wording, name and role you plan to publish, ask for written approval, and keep the reply. Until then use [QUOTE PENDING APPROVAL]. Never edit the words to make them stronger.
  3. Your pilot shows a lower reply time on a small sample over two weeks. How do you write it?
    Model answer"In this pilot, at this company, over these weeks, the median reply time fell from X to Y (n = ... before, ... after)." Add the caveats that apply, such as no control group and a short period. Do not generalise to other companies.
  4. Critique this positioning: "We help all businesses succeed with AI."
    Model answerIt names no customer, no method and no measurable result, so nobody can repeat or check it. Rewrite with the formula: a specific company type and place, a concrete outcome, your method, a result they can verify, and one real difference.
  5. You sent a first outreach message and heard nothing for four days. What next?
    Model answerSend one follow-up that adds something useful, such as a one-page example from their sector. Try another legitimate channel around day 10, send a last note around day 24, then pause for 90 days. Record each step in the tracker.

Course completion checklist

One deliverable per module. Tick each one when it is finished and saved in your portfolio folder.

Further reading