create-pmos 0.1.2 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. package/README.md +97 -9
  2. package/package.json +2 -2
  3. package/payload/AGENTS-HUMAN.md +43 -0
  4. package/payload/AGENTS.md +80 -5
  5. package/payload/KIT-VERSION +2 -2
  6. package/payload/README.md +23 -4
  7. package/payload/okf/core/concepts/health-anchors.md +41 -0
  8. package/payload/okf/core/concepts/index.md +1 -0
  9. package/payload/okf/core/concepts/okr-anti-optimism.md +7 -0
  10. package/payload/okf/core/concepts/okr-tree.md +7 -0
  11. package/payload/okf/core/concepts/output-eval.md +2 -1
  12. package/payload/okf/core/concepts/sdlc-loop.md +2 -1
  13. package/payload/okf/core/templates/content-brief-template.md +70 -0
  14. package/payload/okf/core/templates/design-brief-template.md +1 -1
  15. package/payload/okf/core/templates/eval-rubric-template.md +24 -4
  16. package/payload/okf/core/templates/incident-record-template.md +66 -0
  17. package/payload/okf/core/templates/index.md +3 -0
  18. package/payload/okf/core/templates/prd-template.md +13 -4
  19. package/payload/okf/core/templates/spike-brief-template.md +68 -0
  20. package/payload/planning/evals/agent-run-eval-rubric.md +5 -1
  21. package/payload/planning/okrs/health-budgets.md +7 -0
  22. package/payload/scripts/gate-initiative.sh +50 -11
  23. package/payload/scripts/metrics.py +46 -1
  24. package/payload/scripts/pmos-log.sh +76 -0
  25. package/payload/skills/SKILL-FORMAT.md +103 -1
  26. package/payload/skills/copywriting.skill +55 -0
  27. package/payload/skills/discovery.skill +69 -0
  28. package/payload/skills/eval-rubric.skill +29 -1
  29. package/payload/skills/evaluator.skill +68 -14
  30. package/payload/skills/explain-run.skill +116 -0
  31. package/payload/skills/prd.skill +21 -10
  32. package/payload/state/outcome_checks.jsonl +0 -0
  33. package/payload/templates/agent-run-eval-rubric.md +5 -1
  34. package/payload/templates/product-ci-starter.yml +36 -0
@@ -0,0 +1,66 @@
1
+ ---
2
+ type: Template
3
+ title: Incident Record Template
4
+ description: The scaffold for an incident-lane record (D56) — timeline, impact, root cause, learnings, follow-ups. Authored post-hoc (within 48h of resolution), copied into /planning/prd/ as <id>-incident.md and set as the initiative's prd_path.
5
+ tags: [template, incident, lanes, learning]
6
+ timestamp: 2026-07-17
7
+ ---
8
+
9
+ # Incident Record Template
10
+
11
+ **Situating context:** Authored with [D56](/okf/products/pmos/adr/d56-initiative-lanes.md) to give
12
+ incidents an honest lane: before lanes existed, urgent fixes took the `no-initiative` bypass and
13
+ PMOS's most learning-rich events left **no record at all**. The incident lane inverts eval-first
14
+ by design — fix first, record within 48h — the one sanctioned inversion. Copy the block below into
15
+ `/planning/prd/<id>-incident.md` and fill; set it as the initiative's `prd_path`.
16
+
17
+ > Usage: create the incident initiative (`type: incident`) as soon as work starts — the record may
18
+ > follow the fix, the anchor may not. The 48h window is protocol (AGENTS.md), not CI-enforced:
19
+ > honoring it is what keeps the lane honest.
20
+
21
+ ---
22
+
23
+ ```markdown
24
+ # Incident Record: <short title>
25
+
26
+ ## Identity
27
+ - initiative_id: <id>
28
+ - type: incident
29
+ - product_slug: <product_slug>
30
+ - pm_slug: <pm_slug>
31
+ - parent_KR: <objective/KR reference OR health:<id> (D59)> <!-- REQUIRED — incidents often anchor health:ops. -->
32
+ - severity: critical | major | minor
33
+ - detected: <YYYY-MM-DD HH:MM UTC>
34
+ - resolved: <YYYY-MM-DD HH:MM UTC>
35
+ - recorded: <YYYY-MM-DD> <!-- within 48h of resolution -->
36
+
37
+ ## What happened (timeline)
38
+ <!-- Detection → diagnosis → mitigation → resolution, timestamped. Terse; facts over narrative. -->
39
+
40
+ ## Impact
41
+ <!-- Who/what was affected, for how long, what it cost. -->
42
+
43
+ ## Root cause
44
+ <!-- The actual cause, not the trigger. "Five whys" depth without the ceremony. -->
45
+
46
+ ## Resolution
47
+ <!-- What fixed it — link the PR(s)/commits on branch initiative/<id>. -->
48
+
49
+ ## Learnings
50
+ <!-- What this taught us about the system or the harness. Also append to discoveries.md
51
+ (#friction where applicable) — this section is the retro; discoveries is the searchable log. -->
52
+
53
+ ## Follow-ups
54
+ <!-- Each REAL follow-up is its own lane initiative (usually feature or chore) — listed here by id,
55
+ not silently absorbed into the incident. Empty is honest if the fix is complete. -->
56
+ - <initiative id or "none">
57
+ ```
58
+
59
+ ---
60
+
61
+ ## Authoring checklist
62
+
63
+ - [ ] Recorded within 48h of resolution (the lane's honesty rule).
64
+ - [ ] `parent_KR` present — incidents anchor like everything else.
65
+ - [ ] Root cause is a cause, not a symptom or a person.
66
+ - [ ] Learnings mirrored to discoveries.md; follow-ups are their own initiatives.
@@ -5,4 +5,7 @@ to pick concepts, then fetch the file(s) you need.
5
5
 
6
6
  - [design-brief-template](design-brief-template.md) — The scaffold for a design brief instance — the context handoff from Claude Code to claude.ai/design (leg 1 of the design loop). Copy into /planning/design/ and fill; presentation-only.
7
7
  - [eval-rubric-template](eval-rubric-template.md) — The scaffold for an evaluation rubric instance, including weighted dimensions, partial credit, and the sprint contract. Copy into /planning/evals/ and fill.
8
+ - [incident-record-template](incident-record-template.md) — The incident-lane post-hoc record (D56): timeline, impact, root cause, learnings, follow-ups. Copy into /planning/prd/ as `<id>-incident.md` within 48h of resolution.
8
9
  - [prd-template](prd-template.md) — The scaffold for a Product Requirements Document instance. Copy into /planning/prd/ and fill; do not author PRDs from scratch.
10
+ - [spike-brief-template](spike-brief-template.md) — The discovery-lane spec artifact (D56): question + timebox + spend cap + disposition (learned/promoted/discarded). Copy into /planning/prd/ as `<id>-spike.md`.
11
+ - [content-brief-template](content-brief-template.md) — The content-lane spec artifact (D58): audience, message, voice, sourced claims, deliverables + an optional weighted copy rubric. Copy into /planning/prd/ as `<id>-content-brief.md`.
@@ -14,7 +14,8 @@ same anchoring discipline. Copy the block below into `/planning/prd/<slug>-prd.m
14
14
  copy is an operational instance and does not live in the OKF bundle.
15
15
 
16
16
  > Usage: replace every `<…>` placeholder. Delete guidance comments (`<!-- … -->`) once filled.
17
- > Keep section order. An empty out-of-scope list or a missing parent KR means the PRD is not ready.
17
+ > Keep section order. An empty out-of-scope list or a missing anchor (parent KR, or `health:<id>`
18
+ > per D59) means the PRD is not ready.
18
19
 
19
20
  ---
20
21
 
@@ -25,7 +26,7 @@ copy is an operational instance and does not live in the OKF bundle.
25
26
  - initiative_id: <id>
26
27
  - product_slug: <product_slug>
27
28
  - pm_slug: <pm_slug>
28
- - parent_KR: <objective.kr reference> <!-- REQUIRED. No parent KR = unanchored work, do not proceed. -->
29
+ - parent_KR: <objective.kr reference OR health:<id> (D59)> <!-- REQUIRED. Unanchored work does not proceed. -->
29
30
  - status: draft | approved
30
31
  - date: <YYYY-MM-DD>
31
32
 
@@ -47,6 +48,9 @@ copy is an operational instance and does not live in the OKF bundle.
47
48
 
48
49
  ## Success criteria (outcomes, not output)
49
50
  <!-- Tied to the parent KR. A change in the world, not a list of artifacts produced. -->
51
+ <!-- D57: if a criterion is measurable only AFTER ship, declare it on the initiative as
52
+ outcome_metric + outcome_horizon (create_initiative / initiative frontmatter) — acceptance
53
+ at merge will then NOT be the last word; an Outcome Check disposes it at the horizon. -->
50
54
  - <outcome criterion>
51
55
  - <outcome criterion>
52
56
 
@@ -59,8 +63,12 @@ copy is an operational instance and does not live in the OKF bundle.
59
63
  - rubric: <link to /planning/evals/<slug>-eval-rubric.md>
60
64
 
61
65
  ## Links
62
- - parent KR: <link>
63
- - relevant OKF concepts: <links>
66
+ <!-- Dual-audience rule (D58): ONE situating sentence per link — what it is and why it matters
67
+ here — and quote the parent KR's TEXT, not just its id (health-anchored: name the budget and
68
+ why this work fits its coverage). A stakeholder without the OKF bundle
69
+ must be able to follow every reference. -->
70
+ - parent KR: <link> — *"<the KR text, quoted>"; <why this initiative advances it>*
71
+ - relevant OKF concepts: <link> — *<one situating sentence>*
64
72
  ```
65
73
 
66
74
  ---
@@ -72,4 +80,5 @@ copy is an operational instance and does not live in the OKF bundle.
72
80
  - [ ] Non-goals non-empty.
73
81
  - [ ] Success criteria are outcomes tied to the KR, not output counts.
74
82
  - [ ] Paired eval rubric queued to be written first.
83
+ - [ ] Every link carries one situating sentence; parent-KR text quoted (dual-audience, D58).
75
84
  - [ ] Stays at control altitude — specs *what/why*, not application code or the agent's path.
@@ -0,0 +1,68 @@
1
+ ---
2
+ type: Template
3
+ title: Spike Brief Template
4
+ description: The scaffold for a discovery-lane spike brief (D56) — question, timebox, spend cap, disposition. Copy into /planning/prd/ as <id>-spike.md and set it as the initiative's prd_path; the lighter spec artifact the discovery lane requires instead of a PRD.
5
+ tags: [template, discovery, spike, lanes]
6
+ timestamp: 2026-07-17
7
+ ---
8
+
9
+ # Spike Brief Template
10
+
11
+ **Situating context:** Authored with [D56](/okf/products/pmos/adr/d56-initiative-lanes.md) as the
12
+ discovery lane's spec artifact, resolving the eval-first paradox the SDLC stress test named: a
13
+ prototype exists to *discover* its own done-criteria, so the lane contracts on a **question and a
14
+ timebox**, not on requirements. The [discovery skill](/skills/core/discovery.skill) instantiates
15
+ this template. Copy the block below into `/planning/prd/<id>-spike.md` and fill; the copy is an
16
+ operational instance, not OKF knowledge.
17
+
18
+ > Usage: replace every `<…>` placeholder. An empty question or a missing timebox means the brief is
19
+ > not ready. The disposition section stays open until the spike closes — closing it is mandatory.
20
+
21
+ ---
22
+
23
+ ```markdown
24
+ # Spike Brief: <initiative title>
25
+
26
+ ## Identity
27
+ - initiative_id: <id>
28
+ - type: discovery
29
+ - product_slug: <product_slug>
30
+ - pm_slug: <pm_slug>
31
+ - parent_KR: <objective/KR reference OR health:<id> (D59)> <!-- REQUIRED — anchoring holds in every lane. -->
32
+ - status: open | closed
33
+ - date: <YYYY-MM-DD>
34
+
35
+ ## Question
36
+ <!-- The ONE question this spike answers. If you can write done-criteria instead, it is not a
37
+ spike — take the feature lane. -->
38
+
39
+ ## Why now
40
+ <!-- What decision or initiative is blocked on this answer. -->
41
+
42
+ ## Timebox & spend cap
43
+ - timebox: <e.g. 2 days / 3 runs> <!-- REQUIRED. The spike ends when the box ends, answered or not. -->
44
+ - spend_cap: <e.g. token/infra budget, if relevant>
45
+
46
+ ## Method sketch
47
+ <!-- How you'll attack it — rough is fine; the path is not graded (D14). Throwaway code is
48
+ expected and stays on the initiative branch. -->
49
+
50
+ ## Out of scope
51
+ <!-- What this spike deliberately does NOT try to answer or build. -->
52
+
53
+ ## Disposition (fill at close — mandatory)
54
+ - disposition: learned | promoted | discarded
55
+ - evidence: <what was learned, one paragraph — link artifacts/branch>
56
+ - follow-up: <for `promoted`: link the NEW feature-lane initiative + PRD; a spike never silently
57
+ becomes the product. For `discarded`: one line on why.>
58
+ ```
59
+
60
+ ---
61
+
62
+ ## Authoring checklist
63
+
64
+ - [ ] `parent_KR` present — discovery is anchored work, not free exploration.
65
+ - [ ] The question is singular and answerable inside the timebox.
66
+ - [ ] Timebox stated; the spike ends when it ends (inconclusive is a valid, recordable outcome).
67
+ - [ ] Disposition closed before the initiative leaves the board — `promoted` links a real
68
+ feature-lane PRD; prototype code is deleted or explicitly quarantined, never shipped as-is.
@@ -20,7 +20,11 @@
20
20
  - **Acceptance Gate (PM):** the human PM merges and disposes — final and non-delegable (D12).
21
21
 
22
22
  ## Weighted dimensions (Review Gate)
23
- Outcome-only (D14); partial credit (D15); anchors 0 / 0.5 / 1.0.
23
+ Outcome-only (D14); partial credit (D15); anchors 0 / 0.5 / 1.0. **Evidence rule:** the verdict
24
+ must cite, per scored dimension, a short verbatim quote or precise pointer (file+line, command
25
+ output) from the evaluated artifact; a dimension without evidence is marked `unverified` and
26
+ scored conservatively (≤ 0.5) — never high on trust (evidence-anchored scoring,
27
+ llm-judge-calibration research).
24
28
 
25
29
  | Dimension | Weight | Score | Wtd |
26
30
  |---|---:|---:|---:|
@@ -0,0 +1,7 @@
1
+ # Health budgets (D59) — closed registry, PM-edited only. parent_kr: health:<id>
2
+
3
+ | id | covers | quarterly cap | review trigger |
4
+ |---|---|---:|---|
5
+ | health:tech | refactors, debt, tooling | 20% | over cap / recurring debt theme |
6
+ | health:security | hardening, rotations, audits | 10% | over cap / security work misfiled under a KR |
7
+ | health:ops | pipelines, deploys, monitoring upkeep | 10% | over cap / an ops gap recurring after its fix |
@@ -4,9 +4,15 @@
4
4
  # "anchored" only when:
5
5
  # 1. the branch is initiative/<id> (or the PR body declares 'Initiative: <id>'), and
6
6
  # 2. pmos/state/initiatives/<id>.md EXISTS in the checkout, and
7
- # 3. its prd_path is set and that file exists, and
8
- # 4. its rubric_path is set and that file exists.
9
- # Escape hatch (explicit, logged): the `no-initiative` label skips the gate.
7
+ # 3. the file satisfies its LANE's artifact requirements (D56) — `type:` frontmatter picks it:
8
+ # feature (default) -> prd_path + rubric_path set and both files exist (the original contract)
9
+ # discovery -> prd_path = the spike brief, set and exists; rubric optional
10
+ # chore -> a non-empty `intent:` line in the initiative file; no PRD/rubric
11
+ # incident -> prd_path = the incident record, set and exists (post-hoc is sanctioned)
12
+ # content -> prd_path = the content brief, set and exists; rubric recommended (D58)
13
+ # Unknown/missing type = feature: the strictest lane. Lane variation is never a skip.
14
+ # Escape hatch (explicit, logged): the `no-initiative` label — anchor-free trivia ONLY, real work
15
+ # takes a lane (D56).
10
16
  # Inputs via env: HEAD_REF, PR_BODY, PR_LABELS. Optional: KIT_ROOT (default: pmos).
11
17
  set -euo pipefail
12
18
 
@@ -14,9 +20,9 @@ KIT_ROOT="${KIT_ROOT:-pmos}"
14
20
  fail() { echo "::error::initiative-gate: $*"; echo "❌ initiative gate FAILED — $*"; exit 1; }
15
21
  note() { echo "::notice::initiative-gate: $*"; echo "ℹ️ $*"; }
16
22
 
17
- # ---- escape hatch (genuine chores) — exact label, space-bounded ------------
23
+ # ---- escape hatch (anchor-free trivia only — real work takes a lane) --------
18
24
  case " ${PR_LABELS:-} " in
19
- *" no-initiative "*) note "PR labelled 'no-initiative' — gate skipped (sanctioned chore path)."; exit 0 ;;
25
+ *" no-initiative "*) note "PR labelled 'no-initiative' — gate skipped (anchor-free trivia only; a chore/incident/discovery belongs in its lane, D56)."; exit 0 ;;
20
26
  esac
21
27
 
22
28
  # ---- fail closed if the state layer is missing ------------------------------
@@ -32,7 +38,7 @@ if [ -z "$id" ]; then
32
38
  # below ever prints (silent exit 1 on the most common failure path).
33
39
  id="$(printf '%s' "${PR_BODY:-}" | { grep -ioE 'Initiative:[[:space:]]*[A-Za-z0-9._-]+' || true; } | head -1 | sed -E 's/.*:[[:space:]]*//')"
34
40
  fi
35
- [ -n "$id" ] || fail "Not anchored. Name the branch 'initiative/<id>' or add 'Initiative: <id>' to the PR body, or label the PR 'no-initiative' for a genuine chore. See $KIT_ROOT/AGENTS.md."
41
+ [ -n "$id" ] || fail "Not anchored. Name the branch 'initiative/<id>' or add 'Initiative: <id>' to the PR body, or label the PR 'no-initiative' for anchor-free trivia. See $KIT_ROOT/AGENTS.md."
36
42
 
37
43
  # ---- validate the id (also guards the path interpolation below) -------------
38
44
  printf '%s' "$id" | grep -qE '^[a-z0-9][a-z0-9._-]*$' || fail "Initiative id '$id' is malformed (expected ^[a-z0-9][a-z0-9._-]*$)."
@@ -47,10 +53,43 @@ frontval() { # frontval <key> — first 'key: value' line in the file, value tri
47
53
  }
48
54
  prd="$(frontval prd_path)"
49
55
  rubric="$(frontval rubric_path)"
56
+ lane="$(frontval type)"
57
+ intent="$(frontval intent)"
58
+ anchor="$(frontval parent_kr)"
59
+ [ -n "$lane" ] || lane="feature"
50
60
 
51
- [ -n "$prd" ] || fail "initiative '$id' has no prd_path in $init_file. Author a PRD and set prd_path (eval-first: PRD + rubric before build)."
52
- [ -f "$prd" ] || fail "prd_path '$prd' (from $init_file) does not exist in the repo."
53
- [ -n "$rubric" ] || fail "initiative '$id' has no rubric_path in $init_file. Author an eval rubric and set rubric_path."
54
- [ -f "$rubric" ] || fail "rubric_path '$rubric' (from $init_file) does not exist in the repo."
61
+ # ---- kernel invariant: an initiative anchors (D21/D59) -----------------------
62
+ # Hosted parity: create_initiative rejects a missing parent_kr at row creation; in the kit the
63
+ # file IS the row, so the gate is where the same rule lands. A KR or health:<id> — never blank.
64
+ [ -n "$anchor" ] || fail "initiative '$id' has no parent_kr in $init_file — anchor it to a KR, or to a declared health budget (parent_kr: health:<id> from $KIT_ROOT/planning/okrs/health-budgets.md, D59). Unanchored work is rejected (D21)."
55
65
 
56
- echo "✅ initiative gate PASSED — '$id' is anchored: $init_file exists, PRD ($prd) and rubric ($rubric) present."
66
+ # ---- lane-parameterized artifact checks (D56) --------------------------------
67
+ # The kernel invariants already held above (anchored record; the human merge stays the Acceptance
68
+ # Gate); what varies per lane is the spec/eval artifact. In EVERY lane a rubric_path that is set
69
+ # must point at a real file — optional never means dangling.
70
+ case "$lane" in
71
+ discovery|incident|content)
72
+ # if-form, not `[ … ] && …`: under set -e a false test as a bare && would kill the script.
73
+ what="spike brief"
74
+ if [ "$lane" = incident ]; then what="incident record (post-hoc within 48h is the sanctioned lane rule)"; fi
75
+ if [ "$lane" = content ]; then what="content brief (rubric recommended, not required — D58)"; fi
76
+ [ -n "$prd" ] || fail "initiative '$id' (type $lane) has no prd_path in $init_file — set it to the $what (see $KIT_ROOT/okf/core/templates/)."
77
+ [ -f "$prd" ] || fail "prd_path '$prd' (from $init_file, type $lane) does not exist in the repo."
78
+ [ -z "$rubric" ] || [ -f "$rubric" ] || fail "rubric_path '$rubric' (from $init_file) does not exist in the repo (a rubric is optional in the $lane lane — set the path only to a real file)."
79
+ echo "✅ initiative gate PASSED — '$id' ($lane lane): $init_file exists, $what ($prd) present."
80
+ ;;
81
+ chore)
82
+ [ -n "$intent" ] || fail "initiative '$id' (type chore) has no intent in $init_file — the chore lane requires a one-paragraph 'intent:' line. Anchor-free trivia takes the no-initiative label; real work states its intent."
83
+ [ -z "$prd" ] || [ -f "$prd" ] || fail "prd_path '$prd' (from $init_file) does not exist in the repo (not required in the chore lane — set the path only to a real file)."
84
+ [ -z "$rubric" ] || [ -f "$rubric" ] || fail "rubric_path '$rubric' (from $init_file) does not exist in the repo (not required in the chore lane — set the path only to a real file)."
85
+ echo "✅ initiative gate PASSED — '$id' (chore lane): $init_file exists, intent stated."
86
+ ;;
87
+ *)
88
+ [ "$lane" = "feature" ] || note "unknown initiative type '$lane' on '$id' — enforcing the feature lane (strictest, fail-closed). The enum widens only with a ratified lane decision (D56)."
89
+ [ -n "$prd" ] || fail "initiative '$id' has no prd_path in $init_file. Author a PRD and set prd_path. Feature lane: PRD + rubric before build (eval-first, D13); other work shapes take their lane (D56)."
90
+ [ -f "$prd" ] || fail "prd_path '$prd' (from $init_file) does not exist in the repo."
91
+ [ -n "$rubric" ] || fail "initiative '$id' has no rubric_path in $init_file. Author an eval rubric and set rubric_path."
92
+ [ -f "$rubric" ] || fail "rubric_path '$rubric' (from $init_file) does not exist in the repo."
93
+ echo "✅ initiative gate PASSED — '$id' (feature lane): $init_file exists, PRD ($prd) and rubric ($rubric) present."
94
+ ;;
95
+ esac
@@ -13,7 +13,7 @@ Reads pmos/state/{runs,evals,acceptances}.jsonl and prints:
13
13
 
14
14
  No network, no secrets. Exit 0 always (a reporting tool, not a gate). Anti-optimism: reds print first.
15
15
  """
16
- import json, os, sys
16
+ import datetime, glob, json, os, re, sys
17
17
 
18
18
  KIT = os.environ.get("KIT_ROOT") or os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
19
19
  STATE = os.path.join(KIT, "state")
@@ -97,3 +97,48 @@ else:
97
97
  f"TPR {'n/a' if tpr is None else f'{tpr:.0%}'}, "
98
98
  f"TNR {'n/a — no PM fails seen (leniency blind spot)' if tnr is None else f'{tnr:.0%}'}, "
99
99
  f"kappa {'undefined' if k is None else f'{k:.2f}'} (target >= 0.60 — verdict ADVISORY below it, D40)")
100
+
101
+ # --- D57 outcome checks (accepted-but-unvalidated; due prints red) -----------
102
+ # Declarations live in initiative frontmatter (outcome_metric / outcome_horizon); dispositions are
103
+ # appended to state/outcome_checks.jsonl ({initiative_id, disposition, evidence, date}). The latest
104
+ # line per initiative wins; history is never rewritten. Auto-validation is prohibited (D57) — this
105
+ # section only REPORTS; recording a disposition is always the PM's word.
106
+ checks = load("outcome_checks.jsonl")
107
+ latest_disp = {}
108
+ for c in checks:
109
+ if c.get("initiative_id"):
110
+ latest_disp[c["initiative_id"]] = str(c.get("disposition", "")).lower()
111
+
112
+ declared = []
113
+ for path in sorted(glob.glob(os.path.join(STATE, "initiatives", "*.md"))):
114
+ fm = {}
115
+ for line in open(path, encoding="utf-8"):
116
+ m = re.match(r"^([a-z_]+):\s*(.+?)\s*$", line)
117
+ if m:
118
+ fm.setdefault(m.group(1), m.group(2))
119
+ elif line.strip() and not line.startswith("---"):
120
+ break # frontmatter is the LEADING block only — body text cannot fake a declaration
121
+ if not fm.get("id"):
122
+ if fm.get("outcome_metric"):
123
+ print(f"⚠ {os.path.basename(path)} declares an outcome but has no id: — it can never be disposed; fix the frontmatter")
124
+ continue
125
+ if fm.get("outcome_metric"):
126
+ declared.append(fm)
127
+
128
+ if declared:
129
+ today = datetime.date.today().isoformat()
130
+ undisposed = [f for f in declared if f.get("id") not in latest_disp]
131
+ # a missing horizon errs RED (counts as due): a bet with no judgment date can never come due
132
+ # by itself — horizon-gaming-by-omission (mirrors the hosted CHECK requiring a horizon).
133
+ due = [f for f in undisposed if str(f.get("outcome_horizon", "0000-00-00")) < today]
134
+ pending = [f for f in undisposed if f not in due]
135
+ print()
136
+ if due:
137
+ print(f"🔴 D57 outcome checks DUE ({len(due)}) — accepted bets past horizon, never validated:")
138
+ for f in due:
139
+ print(f" - {f.get('id')}: \"{f.get('outcome_metric')}\" (horizon {f.get('outcome_horizon', '?')})"
140
+ " -> dispose validated|refuted|inconclusive to state/outcome_checks.jsonl")
141
+ refuted = sum(1 for v in latest_disp.values() if v == "refuted")
142
+ print(f"D57 outcomes: {len(declared)} declared — {len(pending)} pending, {len(due)} due, "
143
+ f"{len(latest_disp)} disposed"
144
+ + (f" ({refuted} refuted — recorded, not hidden)" if refuted else ""))
@@ -0,0 +1,76 @@
1
+ #!/usr/bin/env bash
2
+ # PMOS portable kit — pmos-log.sh (D58): the human builder's one-command state writer.
3
+ # Appends ONE validated JSON line to the right pmos/state/ file. Mirrors the hosted rules:
4
+ # verdict enum (save_eval), pm_label enum (record_acceptance), disposition enum + the D57
5
+ # evidence-required-for-terminal rule (record_outcome_check). Append-only by construction — this
6
+ # script only ever `>>`s; a correction is a NEW record. No dependencies beyond POSIX + python3
7
+ # (used solely for safe JSON string escaping).
8
+ #
9
+ # pmos-log.sh run <run_id> <initiative_id> [human_corrections]
10
+ # pmos-log.sh eval <run_id> <initiative_id> <score 0..1> <PASS|PASS-WITH-FINDINGS|FAIL> <rubric_path>
11
+ # pmos-log.sh accept <run_id> <initiative_id> <pass|fail> [note]
12
+ # pmos-log.sh outcome <initiative_id> <validated|refuted|inconclusive> [evidence]
13
+ # pmos-log.sh friction <note...>
14
+ set -euo pipefail
15
+
16
+ KIT="${KIT_ROOT:-$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)}"
17
+ STATE="$KIT/state"
18
+ fail() { echo "pmos-log: ERROR — $*" >&2; exit 1; }
19
+ [ -d "$STATE" ] || fail "state dir not found at $STATE (run from a vendored kit, or set KIT_ROOT)."
20
+
21
+ js() { python3 -c 'import json,sys; print(json.dumps(sys.argv[1]))' "$1"; } # safe JSON string
22
+ nonempty() { # nonempty <label> <value> — ids must not be blank (hosted tools reject them too)
23
+ [ -n "$(printf '%s' "$2" | tr -d '[:space:]')" ] || fail "$1 must not be empty."
24
+ }
25
+ today="$(date -u +%Y-%m-%d)"
26
+
27
+ append() { # append <file> <json-line>
28
+ printf '%s\n' "$2" >> "$STATE/$1"
29
+ echo "✅ appended to state/$1:"
30
+ echo " $2"
31
+ }
32
+
33
+ cmd="${1:-}"; shift || true
34
+ case "$cmd" in
35
+ run)
36
+ [ $# -ge 2 ] || fail "usage: run <run_id> <initiative_id> [human_corrections]"
37
+ nonempty run_id "$1"; nonempty initiative_id "$2"
38
+ corr="${3:-0}"
39
+ printf '%s' "$corr" | grep -qE '^[0-9]+$' || fail "human_corrections must be a non-negative integer, got '$corr'."
40
+ append runs.jsonl "{\"run_id\":$(js "$1"),\"initiative_id\":$(js "$2"),\"human_corrections\":$corr,\"date\":\"$today\"}"
41
+ ;;
42
+ eval)
43
+ [ $# -ge 5 ] || fail "usage: eval <run_id> <initiative_id> <score 0..1> <PASS|PASS-WITH-FINDINGS|FAIL> <rubric_path>"
44
+ nonempty run_id "$1"; nonempty initiative_id "$2"
45
+ printf '%s' "$3" | grep -qE '^(0?\.[0-9]+|0|1(\.0+)?)$' || fail "score must be 0..1 written as 0, 1, N.N or .N — got '$3'."
46
+ case "$4" in PASS|PASS-WITH-FINDINGS|FAIL) ;; *) fail "verdict must be PASS | PASS-WITH-FINDINGS | FAIL, got '$4'." ;; esac
47
+ score="$3"; case "$score" in .*) score="0$score" ;; esac # ".5" -> "0.5": bare .5 is not valid JSON
48
+ [ -f "$KIT/$5" ] || [ -f "$5" ] || fail "rubric_path '$5' not found (relative to the kit or the repo)."
49
+ append evals.jsonl "{\"run_id\":$(js "$1"),\"initiative_id\":$(js "$2"),\"score\":$score,\"verdict\":$(js "$4"),\"rubric_path\":$(js "$5"),\"date\":\"$today\"}"
50
+ ;;
51
+ accept)
52
+ [ $# -ge 3 ] || fail "usage: accept <run_id> <initiative_id> <pass|fail> [note]"
53
+ nonempty run_id "$1"; nonempty initiative_id "$2"
54
+ case "$3" in pass|fail) ;; *) fail "pm_label must be pass | fail, got '$3'." ;; esac
55
+ acc=true; [ "$3" = fail ] && acc=false
56
+ append acceptances.jsonl "{\"run_id\":$(js "$1"),\"initiative_id\":$(js "$2"),\"accepted\":$acc,\"pm_label\":$(js "$3"),\"note\":$(js "${4:-}"),\"date\":\"$today\"}"
57
+ ;;
58
+ outcome)
59
+ [ $# -ge 2 ] || fail "usage: outcome <initiative_id> <validated|refuted|inconclusive> [evidence]"
60
+ nonempty initiative_id "$1"
61
+ case "$2" in validated|refuted|inconclusive) ;; *) fail "disposition must be validated | refuted | inconclusive, got '$2'." ;; esac
62
+ ev="${3:-}"
63
+ if [ "$2" != "inconclusive" ] && [ -z "$(printf '%s' "$ev" | tr -d '[:space:]')" ]; then
64
+ fail "evidence is REQUIRED for a validated/refuted disposition (D57: no rubber-stamps; use 'inconclusive' for an honest non-answer)."
65
+ fi
66
+ append outcome_checks.jsonl "{\"initiative_id\":$(js "$1"),\"disposition\":$(js "$2"),\"evidence\":$(js "$ev"),\"date\":\"$today\"}"
67
+ ;;
68
+ friction)
69
+ [ $# -ge 1 ] || fail "usage: friction <note...>"
70
+ printf -- '- %s %s #friction\n' "$today" "$*" >> "$STATE/discoveries.md"
71
+ echo "✅ appended to state/discoveries.md: - $today $* #friction"
72
+ ;;
73
+ *)
74
+ fail "unknown command '${cmd:-}' — one of: run | eval | accept | outcome | friction (see AGENTS-HUMAN.md)."
75
+ ;;
76
+ esac
@@ -62,6 +62,97 @@ Example reference from a body: *"For multi-product PRDs, follow `prd-multiproduc
62
62
  Use Level 3 to keep Level 2 focused. If a section applies to <20% of runs, it is a Level 3
63
63
  candidate.
64
64
 
65
+ ## Quality — what makes a skill good (not just well-formed)
66
+
67
+ The three levels are *structure*. This section is the *bar*. A skill can be perfectly structured and
68
+ still be junk; these are the tests that keep it worth its context.
69
+
70
+ ### Predictability is the root virtue
71
+
72
+ A skill exists to **wring determinism out of a stochastic system**: it makes the agent take the same
73
+ *process* every run — not produce the same *output* (an evaluator should predictably *judge*; its
74
+ words vary, its behavior doesn't). Every rule below serves predictability; cost and readability are
75
+ symptoms of it, not rival goals. When unsure whether a line belongs, ask: *does it make the agent's
76
+ process more repeatable?* If not, cut it.
77
+
78
+ ### Per-step completion criteria
79
+
80
+ Every procedural step ends on a **checkable done-condition** — the agent must be able to tell done
81
+ from not-done. Where it matters, make the criterion **exhaustive**, not vague: *"every living mirror
82
+ of the rule accounted for"*, not *"produce a change list"*. A fuzzy criterion invites **premature
83
+ completion** — the agent stops when it feels done, not when it is. The
84
+ [semantic-pass rule](/build-skills/evaluator.skill) ("enumerate the live-doc set, then read each for
85
+ meaning — a grep is evidence, never done") is the exemplar this generalizes: it exists because a
86
+ vague "sweep it" criterion let real stale mirrors survive five "clean" passes.
87
+
88
+ ### Pruning — hunt these named failure modes
89
+
90
+ A skill without a pruning discipline only ever grows. Hunt, sentence by sentence:
91
+
92
+ - **No-op** — a line the model already obeys by default, so you pay context to say nothing. Test:
93
+ does it change behavior vs. the default? *"Be thorough"* to an already-thorough agent is a no-op;
94
+ the fix is a stronger word (a leading word), not more words.
95
+ - **Sediment** — stale layers that settle because adding feels safe and removing feels risky. The
96
+ default fate of any skill without this discipline.
97
+ - **Sprawl** — simply too long, even when every line is live. Cure: disclose reference to Level 3;
98
+ split by branch.
99
+ - **Duplication** — the same meaning in more than one place. Costs tokens and maintenance, and
100
+ inflates a meaning's apparent importance. Keep one source of truth.
101
+ - **Negation** — steering by prohibition backfires: *"don't think of an elephant"* names the
102
+ elephant. State the **positive** target behavior; keep a prohibition only as a hard guardrail you
103
+ can't phrase positively, and pair it with what to do instead.
104
+
105
+ **This is the checklist the quarterly harness-assumption review runs** (DECISIONS.md cadence): the
106
+ review's question — *what scaffolding can be removed because the model now handles it natively?* — is
107
+ the no-op and sediment hunt applied across every skill.
108
+
109
+ ### Leading words
110
+
111
+ A **leading word** is a compact concept already in the model's pretraining that anchors a whole
112
+ region of behavior in one token — *tight* (loop), *watermelon*, *default-to-refute*, *silent drift*,
113
+ *Summary Gate*. Repeated across a skill, it accumulates a distributed definition and recruits priors
114
+ the model already holds. A triad spelled out at three sites (*"fast, deterministic, low-overhead"*)
115
+ is begging to **collapse** into one word (*tight*). You win twice: fewer tokens, and a sharper hook
116
+ the agent hangs its behavior on. Hunt every skill for restatements a leading word retires.
117
+
118
+ ### Invocation (advisory)
119
+
120
+ The Agent-Skills world splits skills by *who can invoke them* — **model-invoked** (description always
121
+ loaded, agent can auto-fire it, pays permanent context load) vs **user-invoked** (invisible to the
122
+ agent, human-only, zero context load). PMOS's model differs — skills are **retrieved on demand**
123
+ (see the retrieval rationale under *Ecosystem compatibility* below), so Level-1 frontmatter is the
124
+ always-available trigger and there is no auto-fire to gate. Treat the axis as an authoring lens —
125
+ *is this skill's description earning its keep as an always-available trigger?* — **not** a required
126
+ field. (A formal `invocation:` key is a deferred registry question, not part of this format.)
127
+
128
+ ## Ecosystem compatibility — the Agent Skills standard
129
+
130
+ The open **Agent Skills standard** (Claude Code, Codex, and other harnesses) packages a skill as a
131
+ **directory** `<skill-name>/SKILL.md` with YAML frontmatter (`name`, `description`, optionally
132
+ `license`, `allowed-tools`) plus bundled sub-files. It **validates PMOS's three-level disclosure** —
133
+ same frontmatter-as-trigger, body-as-procedure, sub-files-on-demand shape.
134
+
135
+ PMOS **diverges** deliberately: skills are flat `<name>.skill` files, **not** `<name>/SKILL.md`
136
+ directories. The rationale is PMOS's retrieval model — skills are fetched on demand via `get_skill`
137
+ or referenced from AGENTS.md, **not** auto-discovered by a harness scanning for `SKILL.md`, so the
138
+ directory ceremony buys nothing here. The **mapping is lossless**, which is what keeps the divergence
139
+ cheap:
140
+
141
+ - a `<name>.skill` file **≅** a standard `SKILL.md` body;
142
+ - PMOS's required `name` + `description` frontmatter is **already** the standard's required pair;
143
+ - Level-3 sub-files **≅** the standard's bundled files.
144
+
145
+ (One nuance for the follow-on below: PMOS's *optional* D49 registry keys —
146
+ `version`/`owner`/`risk`/`category`/`scope` — are top-level custom keys; the standard would expect
147
+ producer-defined extensions under a `metadata:` map. The **core** shape is lossless; relocating the
148
+ registry keys belongs to the native-format packaging follow-on, not this flat form.)
149
+
150
+ **Named follow-on (not built here):** shipping the **kit** in native Agent-Skills directory format so
151
+ a stranger's Claude Code / Codex harness auto-discovers PMOS skills. It touches `build-kit.sh`,
152
+ `get_skill`, `gen_skills_seed.sh`, and every skill path, so it is its own initiative — revisit when
153
+ native-discovery demand in kit repos is proven. (This mirrors the mattpocock/skills plugin decision:
154
+ keep the editable flat-file form, add a native-package path only when the demand is real.)
155
+
65
156
  ## File naming
66
157
 
67
158
  - Skill files use the `.skill` extension: `prd.skill`, `eval-rubric.skill`.
@@ -86,4 +177,15 @@ A skill is well-formed when:
86
177
  4. The body (Level 2) carries the full procedure and is self-sufficient for the common case.
87
178
  5. Edge cases are pushed to Level 3 sub-files and referenced by filename.
88
179
 
89
- All five seed skills are authored against this checklist.
180
+ **Quality bar** (the "good", not just "well-formed" — see Quality above):
181
+
182
+ 6. **Predictability served** — every line makes the agent's *process* more repeatable; cut what
183
+ doesn't.
184
+ 7. **Per-step completion criteria** — each procedural step ends on a checkable (and where it matters
185
+ exhaustive) done-condition, not a vague "do X".
186
+ 8. **Pruned** — no no-ops, no sediment, no duplication; prohibitions phrased as positive targets;
187
+ sprawl disclosed to Level 3.
188
+ 9. **Leading words** — restated qualities collapsed into pretrained tokens where one earns its keep.
189
+
190
+ All seed skills are authored against this checklist; the quarterly harness-assumption review re-runs
191
+ items 6–9 across every skill.
@@ -0,0 +1,55 @@
1
+ ---
2
+ name: copywriting
3
+ description: Produce marketing/UX/product copy through the content lane (D58) — content brief first, then draft against audience/message/voice, judged on audience-fit, clarity, voice, and claim accuracy. Use for landing pages, UX microcopy, release notes, docs intros, campaign text; the first domain skill.
4
+ version: 1.0.0
5
+ owner: wawan
6
+ risk: low
7
+ category: domain
8
+ scope: read:okf, write:planning/prd
9
+ ---
10
+
11
+ # copywriting — Produce copy through the content lane
12
+
13
+ The first `/skills/domains/` skill: how copy work runs through PMOS's **content lane**
14
+ ([D58](/okf/products/pmos/adr/d58-builder-profiles.md), on the
15
+ [D56](/okf/products/pmos/adr/d56-initiative-lanes.md) lane substrate). Copy is real, anchored work
16
+ — never the `no-initiative` hatch — but it iterates fast, so the lane contracts on a **brief**,
17
+ and a rubric is *recommended, not required* (a required one recreates fake-rubric pressure).
18
+
19
+ > **Format:** three-level progressive disclosure ([SKILL-FORMAT](/skills/core/SKILL-FORMAT.md)).
20
+
21
+ ## When to use vs. not
22
+
23
+ - **Use** for deliverable text: landing/marketing pages, UX microcopy, release notes, docs
24
+ introductions, campaign/launch copy.
25
+ - **Do not use** for PRDs/specs (prd skill), OKF knowledge (source-to-concept), or code comments.
26
+ - Strategy-of-the-message questions ("should we even say this?") belong to the PM/strategic-brief
27
+ altitude — the brief records the answer; this skill executes it.
28
+
29
+ ## Procedure
30
+
31
+ 1. **Open a content initiative** (`type: content`, anchored like everything else — a KR, or a
32
+ declared health budget `health:<id>` (D59)).
33
+ 2. **Write the content brief** from
34
+ [content-brief-template](/okf/core/templates/content-brief-template.md) into
35
+ `/planning/prd/<id>-content-brief.md`; set it as `prd_path` (the gate requires it). The PM
36
+ approves the brief — that approval is the lane's contract.
37
+ 3. **Draft against the brief**: one variant per stated audience/placement; every factual claim
38
+ traceable to a source named in the brief (claim accuracy is the non-negotiable dimension —
39
+ wrong claims in public copy are incidents, not nits).
40
+ 4. **Judge it.** Recommended: a small weighted rubric (template section in the brief) scoring
41
+ **audience-fit / clarity / voice / claim accuracy**; minimum bar without one: the PM reads the
42
+ draft against the brief line-by-line at the Acceptance Gate.
43
+ 5. **Ship and record** as usual (run log; merge = Acceptance). Reusable phrasing/voice learnings
44
+ go to discoveries; a durable voice guide graduates into OKF as a concept.
45
+
46
+ ## Quality bar
47
+
48
+ Brief approved before drafting; every claim sourced; each audience/placement served by a variant
49
+ (or its absence stated); no invented product facts; the voice section of the brief is followed or
50
+ the deviation is argued in the PR.
51
+
52
+ ## Level 3 sub-cases
53
+
54
+ - None yet. (Localization campaigns and A/B copy variants: author `copywriting-variants.md` on
55
+ first need — variant outcomes belong to D57 outcome checks.)