v0.18.0 — adoption metrics (P4) + AcmeAds vertical demo (P5)
Why
Two priorities-plan strands ship together:
- P4 — adoption metrics.
pmk adoptionanswers "is anyone actually using this?" from local signals (run markers, atom telemetry, audit window), so the kit's value can be judged from data instead of vibes. - P5 — AcmeAds vertical demo. A self-contained way to watch the knowledge loop work end-to-end on a fictional ad-tech workspace, rather than reasoning about it abstractly: seed atoms (P5a) →
pmk demo rundriver (P5b) → a ~15-min walkthrough doc (P5c).
What shipped
pmk adoption— pure report over audit + telemetry + corpus +~/.pmk/adoption.jsonmarkers; five clamped metrics.pmk demo seed|unseed— five approved AcmeAds atoms (placements, vCPM, customer migration, finance terms, onboarding dedup), taggedacme-ads-demo, idempotent.pmk demo run— posts the five guided questions as a real user (PMK_DEMO_USER_TOKEN,chat:writeonly), correlates eachturn.processed, prints a Q→A transcript. Reply-readback uses the gateway bot token'sconversations.replies(bot replies are always threaded).--dm/ zero-config DM auto-open (usesim:history);--channelfor a channel (needschannels:history);--dry-runpreviews.- AcmeAds demo walkthrough (
examples/acme-ads-demo.md) — leads with the zero-credential manual DM path.
Fixed
- Demo
readReplyno longer silently swallowsconversations.replieserrors — a missing bot scope now surfaces in the transcript (e.g.missing_scopeforchannels:history) instead of a generic "did not stabilise".
Dogfood notes (honest)
The knowledge loop was verified live end-to-end: a pmk demo run against the seeded corpus produced five turn.processed events with the correct atoms injected, the right audience tier, and the Q5 escalation boundary (hadMraAsk), with grounded answers (e.g. the vCPM formula sourced from placement_daily).
The smoke also surfaced two host-side, non-code issues worth recording:
- Socket throttling. A backgrounded gateway daemon (
nohup … & disown) got App-Nap/sleep-throttled on macOS, starving Slack's Socket-Mode ping/pong (5s window) → the socket reconnected endlessly but never stayed healthy → a silent multi-day outage with the process alive and the heartbeat ticking. Fix: run undercaffeinate -dimsu. (Hardening follow-up: the gateway's built-incaffeinate -i -t 300is too weak.) - Transcript-capture depends on Slack config. Automated reply-readback needs the bot's
im:history(DMs) orchannels:history(channels), and depends on Slack delivering the demo user's messages. The loop itself is independently verified viaturn.processed; the transcript convenience is gated on the operator's app scopes — now reported honestly.
Test plan
@pmk/cli: 483 tests, 100% pass (npm --workspace packages/cli test).- Live: seeded AcmeAds corpus, ran the five-question demo against the host daemon, confirmed five grounded
turn.processedturns + Q5 escalation.