zynkr-gm · diff

git:20260831.35b07e6 to git:20260913.3eb03e1

18 added, 8 removed. Audit A to A.

---
name: zynkr-gm
sheetId: "0.02"
description: >-
The GM operating-rhythm skill for a small company run from Google Docs/Sheets. Reads the
source-of-record chain in precedence order (strategy alignment section → H2 project tracker
(status SOR) → the ops team's weekly log → OKR & KPI tracker → per-function progress sheets),
derives per-item state (ends-soon · overdue · undated · propose-done · direction-unlabelled),
and produces a fixed-shape WEEKLY GM BRIEF: runway line first, the ≤3 things only the GM can
unblock, two clocks, P0/P1 status by LOB, per-owner rollup, KPI off-target + asks, decisions
register, deliberately-not-this-week, machine health. Delivers as a Gmail draft (interactive)
or an auto-sent email (the Monday cloud routine rendered from this skill). Trigger EAGERLY on
"/zynkr-gm", "zynkr-gm week", "GM 週報", "這週重點", "本週 focus", "本月重點", "what should I
focus on this week", "as a GM what will I do this week", "week focus", "month focus", "GM
brief", "run the GM pass", "哪些 P0 delay 了", "KPI 有沒有 off target", "H2 進度盤點", "幫我盤一下
H2 專案", or any ask for a company-level (not one project's) week/month focus, progress
roll-up, or KPI variance. BOUNDARY — do NOT hijack: /project-status-update (one project's
weekly status email from its own tracker), /consult-status-report and /consult-governance
(consulting portfolio), /admin-governance (file-level Drive↔local index drift),
/admin-meeting-prep (per-meeting packets), /skill-finder (which skill for a task),
/planning-tracker-sync (the TEAM weekly tracker block, nudges and snapshots — it consumes this
skill's derived-state rules, but the founder brief stays here), the rest of the planning suite
0.03-0.10 (a planning CYCLE: pre-work, offsite synthesis, tracker build, OKR reconcile),
/curate-livestream-transcripts (it produces the notes this skill only health-checks). This
skill reads broadly and writes narrowly: it never edits the tracker, the onboarding master
doc, or another owner's doc.
category: strategy
project: zynkr-gm
platform: claude
status: WIP
author: Peter Tu
input: "Optional sub-command (week | progress [LOB] | send) and an optional 'as of' date; identifiers come from the private config (~/.config/zynkr/gm.json) or the rendered routine prompt."
process: "Anchor on today → load private config → idempotency check → read the SOR chain in precedence order (script-first) → derive per-item state + per-owner rollup → compose the fixed-shape brief, every number cited → draft or send → report."
output: "Weekly GM brief in English as an HTML email: runway · ≤3 decision cards, each with options and a link to the closing cell · two clocks · P0/P1 by LOB · owners · KPI · decisions"
synergy: [admin-governance, project-status-update, consult-governance, skill-finder, curate-livestream-transcripts, admin-meeting-prep, planning-tracker-sync, planning-evidence-pack]
---
# zynkr-gm
```bash
npx skills add https://github.com/peter-tu-zynkr/zynkr-skill-builder --skill zynkr-gm
```
`zynkr-gm` is the general manager's Monday. It reads the company's source-of-record chain in
the order the knowledge directory says wins (strategy alignment → project tracker → weekly ops
log → OKR/KPI sheet → per-function progress sheets), works out which H2 items are moving,
stuck, undated or about to expire, and turns that into one fixed-shape brief whose first line
is always runway and whose second block is always the ≤3 things only the GM can unblock. It
exists to replace a ~40-tool-call manual read pass, and to be the instrument for "we have no
instrumentation": the KPI dashboard's Actual column, the missing decide-by dates, the P0s
nobody put dates on. It **reads broadly and writes narrowly** — the brief is the only output;
it never edits the tracker, the onboarding master doc, or another owner's document.
---
## Configuration (private — never in this repo)
This repository is public. The skill's method is here; the **identifiers are not**. At
runtime the skill loads `~/.config/zynkr/gm.json` (schema and placeholder values in
`references/config.example.json`; see `references/config.README.md`). The cloud routine gets
the same values baked into its prompt by `scripts/render_routine_prompt.py` from
`references/routine-prompt.tmpl`. If the config file is missing in an interactive run, ask for
it — do not guess IDs and do not proceed with placeholders.
| Config key | Role (see `references/source-map.md`) |
|---|---|
| `google_account` | account for all `google-workspace` MCP calls |
| `recipients[]` | who receives the brief (default: the GM only) |
| `sources.main_tracker` | H2 project tracker sheet + tab — **the status source of record** |
| `sources.ops_weekly` | the ops lead's weekly log doc (newest week on top) — read-only |
| `sources.okr_kpi_tracker` | OKR & KPI tracker (tabs `okrs`, `kpi_dashboard`) — the metrics SOR |
| `sources.ops_heal_tracker` | ops gap-heal action sheet (tabs `fixes`, `decisions`) |
| `sources.course_tracker` | flagship course project tracker (milestone dates only) |
+ | `sources.finance_ledger` | **the books** — runway · burn · cash-basis revenue. Read-only |
| `sources.vms_v2` · `sources.integrated_refresh` | strategy docs — read the authoritative TOP section only |
| `sources.plan_docs.<lob>` · `sources.eae_readme` | per-function H2 plan docs — Refresh block only, on change |
| `sources.knowledge_directory` · `sources.org_taxonomy` | governance docs (SOR precedence table, DRIs) |
| `sources.livestream_notes_folder` | health-check only: date of the newest weekly note |
- | `constraints.*` | runway floor (months), books-stale days, burn cap, Q3/H2 end dates |
+ | `constraints.*` | runway floor (months), books-stale days, burn cap, **burn window**, Q3/H2 end dates |
| `routine.*` | trigger id, cron, connectors, model, subject prefix — for `render_routine_prompt.py` |
---
## Required reading (before acting)
| File | Read when |
|---|---|
| `references/source-map.md` | Always — what each source is, which section/tab is authoritative, precedence, read policy, cloud vs local availability |
| `references/brief-template.md` | Always — the exact 00–08 anatomy, headings, HTML skeleton, evidence-citation rule |
| `references/derived-state-rules.md` | Always — ENDS_SOON / OVERDUE / UNDATED / PROPOSE_DONE / DIRECTION_UNLABELLED / STALLED, priority weighting |
| `references/kpi-map.md` | When composing block 05 — per-metric class (AUTO/SEMI/HUMAN), where an Actual comes from, cloud availability |
| `references/lob-skills-seed.md` | When a "what's next" needs an instrument — chain order, which skill moves which P0, and the category-0 weekly boundary. **Which skills exist is NOT in this file** — read it from `sources.skills_knowledge_map` (the skills KB Doc), one category section per LOB |
---
## Sub-commands & cadence
| Invocation | What runs | Cadence |
|---|---|---|
| `/zynkr-gm` (no arg) | Monday → `week`; 1st of month → `month` (P2, not yet built) then `week`; otherwise ask which | — |
| `/zynkr-gm week` | full read pass → weekly brief → Gmail **draft** | interactive |
| `/zynkr-gm week send` | same, but **sends** to `recipients` (idempotent per ISO week) | scheduled / explicit |
| `/zynkr-gm progress [LOB]` | Steps 1–4 only, printed in chat; optional LOB filter (`1.0` … `8.0`) | on demand |
| Monday cloud routine | `week send` rendered as a self-contained prompt for the claude.ai routine (Drive + Gmail connectors) | Mon 09:00 local |
`kpi` (writes Actuals), `month` (monthly review + quarter/half escalation) and `learn`
(knowledge-directory curation) are P1–P3 — see Limitations.
---
## Step 1 — Anchor on today and resolve the mode
Determine today's date in `timezone` (default Asia/Taipei). The **ISO week** containing today
is the brief's key (e.g. `2026-W34`); the week window is Monday→Sunday. If the user gave an
"as of" date, use that instead. Resolve the sub-command from the args table above.
Compute the two clocks up front: **cash clock** (runway months, from Step 3.4) and
**calendar clock** (days to `constraints.q3_end` and `constraints.h2_end`, plus any P0/P1
whose 結束 falls within 30 days).
## Step 2 — Load config and check idempotency
1. Read `~/.config/zynkr/gm.json`. Missing → stop and ask for it (interactive) / fail loud
(unattended).
2. Idempotency (only when the mode will **send**): search Gmail
`in:sent newer_than:8d` and read subjects; if one starts with
`routine.subject_prefix` and carries this ISO week's key, report
`This week's GM brief has already been sent — skipping (idempotent).` and stop. Do not
search by CJK subject text —
list recent sent mail and match the prefix + week key yourself.
## Step 3 — Read the source-of-record chain (precedence order, script-first)
Never load a whole large doc into context. Big docs are dumped to a file by the harness when
the tool result exceeds the limit — slice them with the scripts. Read in this order and stop
reading a source once you have what the brief needs.
### 3.0 Resolve link targets first (every run, one call per spreadsheet)
The brief links every source it names (template C1), and a link to a row or cell needs the
tab's numeric `gid`. Before reading values, call `mcp__google-workspace__get_spreadsheet_info`
once per spreadsheet in `sources.*` that the brief will cite — it returns each tab's ID — and
keep a `{spreadsheet → {tab → gid}}` map for the rest of the run. Then a cell link is
`https://docs.google.com/spreadsheets/d/<id>/edit#gid=<gid>&range=<col><row>`.
Track the **row number** alongside every row you keep (sheet rows are 1-indexed and row 1 is
the header, so the first data row is 2), and the **column letter** of any cell the brief will
ask the reader to type into — `決議紀錄` and `狀態` on the decisions tab, `結束` on the tracker.
Without those two facts the brief can only link to a file, which is the weaker fallback.
In the cloud routine the Drive connector does not expose gid: link the file, name the tab and
row in the text, and note the degradation once in block 08.
### 3.1 Tracker — the status source (every run)
`mcp__google-workspace__read_sheet_values` on `sources.main_tracker.id`, range
`<tab>!A1:M200`. Keep rows whose `#` looks like `N.NN` and whose 項目 is non-empty (rows like
`1.0` with an empty 項目 are category headers). Save as JSON rows (keys = header cells) to the
dump dir. Status vocabulary is 未開始 · 進行中 · 放棄 · 完成 · 暫停 (完成 · 暫停 added 2026-08-24).
完成 and 放棄 are **terminal** — exclude them from open-work counts and from owner load; 暫停 keeps
its owner but carries no schedule. 延遲 still does not exist and stays **derived** (Step 4).
### 3.2 What actually happened — the ops weekly log (every run)
`mcp__google-workspace__get_doc_as_markdown` on `sources.ops_weekly.id` with
`include_comments=false`. The result is large and lands in a file → run
`python3 scripts/extract_newest_block.py <file> --blocks 2 --json`. Block 1 = the newest
week; block 2 is only for stall detection. The block has a fixed skeleton (Team update ·
Demand Marketing · AI consulting & Sales · Operation · Knowledge product · AI assistant dev ·
Tech/People/Finance, each with `Metrics` bullets, mostly bare `#` placeholders). Pull: event
list with registration counts, course milestone checklist, anything under `#Team update`.
**Never write to this doc.**
### 3.3 Measures — OKR & KPI tracker (every run)
`read_sheet_values` on `sources.okr_kpi_tracker.id`, tabs `okrs` and `kpi_dashboard`
(the `Initiatives` tab is a stale mirror of the tracker — ignore it). Resolve columns by
header text (`Tracker #`, `Q3 target`, `Q4 target`, `Actual`, and — if present — `As of`,
`Source`). Use `scripts/kpi_locate.py` when you need a specific row.
### 3.4 Function-specific progress sheets (every run)
- `sources.ops_heal_tracker`: tabs `fixes` (修復清單: # · 波次 · 動作 · 嚴重度 · 目標 · 負責人 ·
狀態) and `decisions` (待決事項: # · 事項 · 決策者 · 期限/備註 · 狀態 · 決議紀錄). Open
決策 rows feed block 06.
- `sources.course_tracker`: tab per config; take stage/milestone rows with End dates and
Status only (this skill does **not** produce that project's status email —
/project-status-update does).
+ - `sources.finance_ledger` (Zynkr Finance Ledger): tab `Monthly Summary` — the books, and the
+ runway/burn source since 2026-09-13. Readable in **both** environments, so the runway line is no
+ longer environment-dependent. **Read-only**; never append, and never write into the
+ `Financial Model` tab's month columns (spilled arrays → `#REF!`).
- Local runs may add CRM (`mcp__zynkr__list_deals`, `list_tasks`) for sales/consulting
- evidence and the accounting database for runway/burn per `references/kpi-map.md`.
- In the cloud routine these are unavailable — the brief marks those lines
+ evidence. In the cloud routine the CRM is unavailable — the brief marks those lines
`(排程執行無法讀取 · 待本地補)` rather than guessing.
- - Runway: from the KPI dashboard's runway/burn rows (`Actual` + `As of`) or the accounting
- read. If neither exists → the runway line reads **RED · 未計量** and says so.
+ - Runway: computed from the ledger per `references/kpi-map.md` § *How to compute it* — cash =
+ cumulative `total` at the last closed month (**not** the 富邦 bank balance), burn = mean `net`
+ over `constraints.burn_window`. The KPI Dashboard's runway/burn `Actual` cells are a **mirror**,
+ not the source: if they disagree with the ledger, the ledger wins and block 05 asks Peter to
+ refresh them. ⚠ While `constraints.burn_window` is null the brief prints **both** candidate
+ numbers and says which ruling would settle it — it never picks one silently. If the ledger
+ itself cannot be read → **RED · 未計量**.
### 3.5 Strategy + plan docs (only on change)
Use `mcp__google-workspace__search_drive_files` (or file metadata) to get `modifiedTime` for
`sources.vms_v2`, `sources.integrated_refresh`, each `sources.plan_docs.<lob>` and
`sources.eae_readme` (key on the **target** doc IDs, not folder shortcuts). Read a doc only if
it changed within the last 7 days (P0; the P1 `doc-watch` tab replaces this with last-seen
timestamps) — and then read **only** the authoritative section named in `source-map.md`
(the TOP alignment / Refresh block). Otherwise the fixed facts in config (`constraints.*`)
and `references/` are enough. Any change here goes into block 08 as "SOR doc changed".
### 3.6 Machine health + calendar (cheap)
- `sources.livestream_notes_folder`: list files, take the newest `逐字稿_*` date; if older
than 8 days → flag "weekly curation did not run".
- Calendar (local only, if the Calendar API is enabled): this week's events as anchors.
Skip silently in unattended mode.
## Step 4 — Derive state, then roll up
Run `python3 scripts/derive_state.py rows.json --today <YYYY-MM-DD> [--prev prev_rows.json]
--json`. It emits per-item states (ENDS_SOON · OVERDUE · UNDATED · CHANGED · PROPOSE_DONE)
and a per-owner rollup. Apply `references/derived-state-rules.md` for the judgement calls the
script cannot make: DIRECTION_UNLABELLED (a P0 whose direction lacks 已定案/還在摸索),
STALLED (needs two snapshots — mark "not available yet" until P1), and priority weighting
(P0 UNDATED for >2 weeks → escalate into block 01; P2 issues → status table only).
## Step 5 — Compose the brief
Follow `references/brief-template.md` **exactly**, in order:
- **00 Runway line** — always first. Months · burn vs `constraints.burn_cap_ntd_per_month` ·
books as-of. RED if < `runway_floor_months` or as-of older than `books_stale_days`, or if
un-metered.
- **01 ≤3 decisions only the GM can close** — from open decisions past/near their decide-by,
P0s owned by the GM with 結束 ≤14d, and P0 UNDATED >2 weeks. Three at most. Each one is a
**decision card** in the exact shape the template gives: one question ending in `?` · real
options A/B (or the exact value and cell to fill) · the consequence of not deciding · the
single action that closes it, hyperlinked to the cell · where it came from. This block is
the test of whether the brief changes the week — see the template's D1–D5 before writing it.
- **02 Two clocks** — cash clock + calendar clock (Q3/H2 end + P0/P1 ending ≤30d).
- **03 P0/P1 status by LOB** — state · evidence (tracker Δ / weekly-log mention / sheet
activity) · next. Coarse on purpose.
- **04 Per-owner rollup** — for each owner: their P0s · missing dates · asks · last heard
from (newest weekly-log mention). Include a GM-load line (share of P0s held by the GM).
- **05 KPI off-target + asks** — only rows with both a target and an Actual can be
off-target; rows without an Actual are listed as asks to their owner (batch, don't nag).
- **06 Decisions register** — open 決策 rows + strategy-doc open decisions + inferred ones,
each with label (已定案/還在摸索/未標), owner, decide-by (or "無期限"), status.
- **07 Deliberately not this week** — named skips.
- **08 Machine health** — routines that did/didn't fire, SOR docs that changed, sources the
run could not read.
Rules while composing (the template states these in full as C1–C5 and D1–D5 — read them, they
are the difference between a brief that gets acted on and one that gets skimmed):
- **English prose, verbatim source strings.** Write the brief in English. Never translate a
string the reader must match against a source — item names, 狀態 values, tab names, doc
titles, column headers stay in their original script, glossed once in parentheses.
- **Link, don't cite.** Every doc, sheet, tab, row and cell named anywhere in the brief is a
hyperlink to that exact place; the `(來源:… · 讀取於 …)` parenthetical is retired. Show an
as-of date only where it changes the reading (state-driving numbers, stale sources).
- **A decision needs options.** If you cannot state the alternatives, it is research, not a
decision — it does not belong in block 01.
- **Open the source before writing the question.** A decision row's own shorthand is not a
summary; resolve it to the real choice, or report under C5 that it could not be resolved.
- **A dead reference is a finding.** A `[N.N]` code or `§` section that resolves to no real
document goes to block 08 as a dead reference — never passed through as though it were an
address.
- Never restate numbers from narrative docs; never invent a HUMAN metric; when a source could
not be read, say so in 08 instead of filling in.
- Respect the word budgets. If block 03 is over, it is carrying prose that belongs nowhere.
## Step 6 — Deliver
Render the brief as an HTML email with **all CSS inline** (Gmail strips `<style>`), using the
skeleton and palette notes in `references/brief-template.md`. Subject:
`<routine.subject_prefix><YYYY-MM-DD> (W<ww>) — <one-line focus>`, the focus clause in plain
English, ≤10 words, naming the thing at stake rather than its code.
The plain-text alternative part carries the 00 line and each 01 card in full (question,
options, close-it action with the raw URL). It is what the reader sees on a watch or in a
preview pane — it must be answerable on its own, never "see the HTML version".
- Interactive `week` → `mcp__google-workspace__draft_gmail_message` to `recipients` (draft,
never send) **and** print the brief in chat.
- `week send` (explicit or scheduled) → send exactly one message to `recipients`; if the
send tool is refused, fall back to a draft and say so in the report.
- `progress` → chat only.
## Step 7 — Report back
One paragraph: ISO week · delivered as draft/sent (+ id) · the 01 list verbatim · counts
(P0 overdue / undated / ends-soon) · anything unreadable. If idempotency skipped the send,
say exactly that.
---
## Unattended / scheduled runs (the Monday routine)
The cloud routine is this skill rendered into one self-contained prompt:
`python3 scripts/render_routine_prompt.py --config ~/.config/zynkr/gm.json
--template references/routine-prompt.tmpl --out <rendered.md>` — then create/update the
claude.ai routine with that prompt (`routine.*` in config: cron, environment, Drive + Gmail
connectors, model). Differences from a local run, by design:
- Reads go through the **Google-Drive connector** (`read_file_content` returns Sheets as
markdown tables and Docs as text; large docs may be truncated — the ops weekly log's newest
- week is at the top, so that is safe). No CRM, no accounting, no calendar → those lines are
- marked unavailable, never guessed.
+ week is at the top, so that is safe). The finance ledger IS readable here, so runway is computed
+ on scheduled runs too. No CRM, no calendar → those lines are marked unavailable, never guessed.
- Delivery is **send** via the Gmail connector (`send_message`); if refused → `create_draft`
and the run report says so. Idempotent per ISO week via the sent-mail check.
- Ask nothing; fail loud (a missing-data brief is worse than a visible failure); one message
max.
- When the skill changes, **re-render and update the routine** — the prompt is derived, not
hand-edited. That is the whole point of the template.
---
## Guardrails
- Never edit the H2 tracker's status, dates or owners — owners push schedules.
- Never write to the onboarding master doc or any shared fact; propose to the GM instead.
- Never write into another owner's doc (the ops weekly log is read-only for this skill).
- Every number cites source + as-of; HUMAN metrics are asked, not invented.
- Precedence is enforced, not debated: strategy alignment section > body; Refresh block >
plan body; tracker > OKR sheet > plan docs. Deprecated paths in the knowledge directory
stay deprecated.
- Read caps: no whole-doc loads; plan docs only on change; target ≲100k tokens per run.
- Unattended: draft-fallback, ask nothing, fail loud, idempotent per ISO week.
## Outputs
- The weekly GM brief (HTML email: draft or sent) — see `references/brief-template.md`.
- In chat: the same brief in markdown, plus the Step 7 report.
- (P1+) rows in the OKR & KPI tracker's `gm-weekly` / `kpi-history` / `decisions` /
`doc-watch` tabs; KPI `Actual` cells for AUTO/SEMI metrics.
## Reference files
- `references/source-map.md` · `references/derived-state-rules.md` ·
`references/brief-template.md` · `references/kpi-map.md` · `references/lob-skills-seed.md`
- `references/routine-prompt.tmpl` · `references/config.example.json` ·
`references/config.README.md`
- `scripts/extract_newest_block.py` · `scripts/derive_state.py` · `scripts/tracker_diff.py`
· `scripts/kpi_locate.py` · `scripts/render_routine_prompt.py` (all stdlib; `--selftest`)
## Limitations (v1 = P0)
- **Read-only.** No KPI cell writes, no state tabs yet → STALLED cannot be computed (needs
two snapshots) and "changed since last week" relies on `--prev` if you saved last week's
rows. P1 adds the tabs and `kpi` writes; P2 adds `month` (+ Q3/H2 checkpoint scoring); P3
adds `learn` (knowledge-directory curation, propose → `--apply` interactive only).
- The tracker's status vocabulary has no 延遲; overdue and undated are inferred from
dates that are often placeholders — the brief makes that visible rather than hiding it.
- - Cloud runs cannot see CRM/accounting/calendar; those lines say so.
+ - Cloud runs cannot see CRM/calendar; those lines say so. (Runway is no longer in that set — the
+ finance ledger is a Drive Sheet the connector can read.)
- The brief is company-level. One project's status email is `/project-status-update`.
## Self-checks
- Runway line present and first? Un-metered → RED, not blank?
- Block 01 has ≤3 items, each something only the GM can do?
- Every P0 appears in 03 exactly once, with a state?
- Any HUMAN metric invented? (must be none)
- Unattended: exactly one send or one draft; idempotency respected; nothing asked?
**Plain-language check** (the template's closing section, run before delivering — any "no" is a
rewrite, not a note):
- Does every card in 01 ask **one** question, ending in a question mark?
- Could the reader answer each one **without opening another document**?
- Does every card offer real **options** — or name the exact value and the exact cell?
- Is every document, tab, row and cell mentioned anywhere in the brief a **hyperlink**?
- Is there any `[N.N]` code, `§` reference or acronym that is never expanded?
- Is any reference **dead**, and reported in 08 as a finding rather than passed through?
- Is the brief in English, with source strings left verbatim and glossed once?
- Read block 01 aloud: does it sound like a person telling you what they need?