create-pmos 0.43.0 → 0.44.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 (32) hide show
  1. package/bin/cockpit-install.js +130 -0
  2. package/bin/create-pmos.js +47 -4
  3. package/package.json +1 -1
  4. package/payload/AGENTS-HUMAN.md +10 -25
  5. package/payload/AGENTS.md +4 -0
  6. package/payload/KIT-VERSION +1 -1
  7. package/payload/mods/.claude-plugin/marketplace.json +12 -0
  8. package/payload/mods/pmos-cockpit/.claude-plugin/plugin.json +9 -0
  9. package/payload/mods/pmos-cockpit/README.md +60 -0
  10. package/payload/mods/pmos-cockpit/hooks/hooks.json +1 -0
  11. package/payload/mods/pmos-cockpit/hooks/lib/commands.mjs +53 -0
  12. package/payload/mods/pmos-cockpit/hooks/lib/grants.mjs +88 -0
  13. package/payload/mods/pmos-cockpit/hooks/lib/guard.mjs +561 -0
  14. package/payload/mods/pmos-cockpit/hooks/lib/initiative.mjs +54 -0
  15. package/payload/mods/pmos-cockpit/hooks/lib/layout.mjs +31 -0
  16. package/payload/mods/pmos-cockpit/hooks/lib/profile.mjs +17 -0
  17. package/payload/mods/pmos-cockpit/hooks/lib/skills.mjs +69 -0
  18. package/payload/mods/pmos-cockpit/hooks/register.tsx +460 -0
  19. package/payload/mods/pmos-cockpit/types/index.d.ts +34 -0
  20. package/payload/planning/evals/eval-calibration.md +1 -1
  21. package/payload/planning/okrs/health-budgets.md +1 -1
  22. package/payload/scripts/pmos-log.sh +119 -7
  23. package/payload/skills/pmos-router.skill +1 -1
  24. package/payload/skills/prototype.skill +1 -1
  25. package/payload/skills/strategic-brief.skill +1 -1
  26. package/payload/skills-native/harness-refine/SKILL.md +1 -1
  27. package/payload/skills-native/pmos-router/SKILL.md +1 -1
  28. package/payload/skills-native/prototype/SKILL.md +1 -1
  29. package/payload/skills-native/strategic-brief/SKILL.md +1 -1
  30. package/payload/state/discoveries.md +1 -1
  31. package/workspace/package-lock.json +1 -1
  32. package/workspace/package.json +1 -1
@@ -3,18 +3,29 @@
3
3
  # Appends ONE validated JSON line to the right pmos/state/ file. Mirrors the hosted rules:
4
4
  # verdict enum (save_eval), pm_label enum (record_acceptance), disposition enum + the D57
5
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
6
+ # script only ever appends; a correction is a NEW record. Parity with PMOS's own writers
7
+ # (pmos-cockpit-mod, D81): an acceptance needs a note and a run that exists, and a ledger that is a
8
+ # symlink or has another hard link is refused (the same rule as scripts/lib/sink.js, applied at the
9
+ # moment of the write; it does not defend against a process racing the open). No dependencies beyond POSIX + python3
7
10
  # (used solely for safe JSON string escaping).
8
11
  #
9
12
  # pmos-log.sh run <run_id> <initiative_id> [human_corrections] [model]
10
13
  # pmos-log.sh eval <run_id> <initiative_id> <score 0..1> <PASS|PASS-WITH-FINDINGS|FAIL> <rubric_path> [evaluator_model]
11
- # pmos-log.sh accept <run_id> <initiative_id> <pass|fail> [note]
14
+ # pmos-log.sh accept <run_id> <initiative_id> <pass|fail> <note>
12
15
  # pmos-log.sh outcome <initiative_id> <validated|refuted|inconclusive> [evidence]
13
16
  # pmos-log.sh friction <note...>
17
+ # pmos-log.sh grant <merge:<PR or branch>|publish:<artifact>> <pm> <instruction> [ttl_minutes]
18
+ # pmos-log.sh grant-use <grant_id> <action>
19
+ #
20
+ # `grant` and `grant-use` belong to the cockpit mod (D81). A grant is the PM's typed permission for
21
+ # ONE action: single use, 120 minutes unless a shorter time is given (24 hours at most), one target. A
22
+ # standing scope (docs-sync, outward:*) and any other scope is refused. The mod mints a grant only from
23
+ # the PM's typed /pmos-grant or a press, and denies a model that runs either subcommand.
24
+ # PMOS_STATE_DIR names the folder of the ledgers when it is not <kit>/state (PMOS's own repository).
14
25
  set -euo pipefail
15
26
 
16
27
  KIT="${KIT_ROOT:-$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)}"
17
- STATE="$KIT/state"
28
+ STATE="${PMOS_STATE_DIR:-$KIT/state}"
18
29
  fail() { echo "pmos-log: ERROR — $*" >&2; exit 1; }
19
30
  [ -d "$STATE" ] || fail "state dir not found at $STATE (run from a vendored kit, or set KIT_ROOT)."
20
31
 
@@ -24,8 +35,54 @@ nonempty() { # nonempty <label> <value> — ids must not be blank (hosted tools
24
35
  }
25
36
  today="$(date -u +%Y-%m-%d)"
26
37
 
38
+ # safe_append <file under state/> <line> — open with O_NOFOLLOW, then refuse anything that is not a
39
+ # regular file with a single link, and write to the descriptor that was vetted (python3 is already a
40
+ # dependency of this script). Nothing is written when it refuses.
41
+ safe_append() {
42
+ python3 - "$STATE/$1" "$2" <<'PY' || exit 1
43
+ import os, stat, sys
44
+ path, line = sys.argv[1], sys.argv[2]
45
+ flags = os.O_WRONLY | os.O_APPEND | os.O_CREAT | getattr(os, "O_NOFOLLOW", 0) | getattr(os, "O_NONBLOCK", 0)
46
+ try:
47
+ fd = os.open(path, flags, 0o644)
48
+ except OSError as e:
49
+ sys.stderr.write("pmos-log: ERROR — refusing to write %s: %s\n" % (path, e.strerror)); sys.exit(1)
50
+ try:
51
+ st = os.fstat(fd)
52
+ if not stat.S_ISREG(st.st_mode) or st.st_nlink != 1:
53
+ sys.stderr.write("pmos-log: ERROR — refusing to write %s: it is not a regular file with a single link (a symlink, a hard link or a special file).\n" % path); sys.exit(1)
54
+ os.write(fd, (line + "\n").encode("utf-8"))
55
+ finally:
56
+ os.close(fd)
57
+ PY
58
+ }
59
+
60
+ # run_exists <run_id> — true when state/runs.jsonl holds a run with that id. Lines that are not JSON
61
+ # are skipped; the checkers report them.
62
+ run_exists() {
63
+ python3 - "$STATE/runs.jsonl" "$1" <<'PY'
64
+ import json, sys
65
+ path, rid = sys.argv[1], sys.argv[2]
66
+ try:
67
+ lines = open(path, encoding="utf-8").read().splitlines()
68
+ except OSError:
69
+ sys.exit(1)
70
+ for l in lines:
71
+ l = l.strip()
72
+ if not l:
73
+ continue
74
+ try:
75
+ r = json.loads(l)
76
+ except ValueError:
77
+ continue
78
+ if isinstance(r, dict) and str(r.get("run_id", "")) == rid:
79
+ sys.exit(0)
80
+ sys.exit(1)
81
+ PY
82
+ }
83
+
27
84
  append() { # append <file> <json-line>
28
- printf '%s\n' "$2" >> "$STATE/$1"
85
+ safe_append "$1" "$2"
29
86
  echo "✅ appended to state/$1:"
30
87
  echo " $2"
31
88
  }
@@ -86,9 +143,11 @@ case "$cmd" in
86
143
  append evals.jsonl "{\"run_id\":$(js "$1"),\"initiative_id\":$(js "$2"),\"score\":$score,\"verdict\":$(js "$4"),\"rubric_path\":$(js "$5"),\"date\":\"$today\"$emodel_field}"
87
144
  ;;
88
145
  accept)
89
- [ $# -ge 3 ] || fail "usage: accept <run_id> <initiative_id> <pass|fail> [note]"
146
+ [ $# -ge 4 ] || fail "usage: accept <run_id> <initiative_id> <pass|fail> <note> — the note is required: an acceptance with no reasoning is the rubber stamp D12 exists to prevent."
90
147
  nonempty run_id "$1"; nonempty initiative_id "$2"
91
148
  case "$3" in pass|fail) ;; *) fail "pm_label must be pass | fail, got '$3'." ;; esac
149
+ nonempty note "$4"
150
+ run_exists "$1" || fail "run_id '$1' is not in state/runs.jsonl — log the run first (pmos-log.sh run ...), then accept it."
92
151
  acc=true; [ "$3" = fail ] && acc=false
93
152
  append acceptances.jsonl "{\"run_id\":$(js "$1"),\"initiative_id\":$(js "$2"),\"accepted\":$acc,\"pm_label\":$(js "$3"),\"note\":$(js "${4:-}"),\"date\":\"$today\"}"
94
153
  ;;
@@ -104,10 +163,63 @@ case "$cmd" in
104
163
  ;;
105
164
  friction)
106
165
  [ $# -ge 1 ] || fail "usage: friction <note...>"
107
- printf -- '- %s %s #friction\n' "$today" "$*" >> "$STATE/discoveries.md"
166
+ safe_append discoveries.md "- $today $* #friction"
108
167
  echo "✅ appended to state/discoveries.md: - $today $* #friction"
109
168
  ;;
169
+ grant)
170
+ [ $# -ge 3 ] || fail "usage: grant <merge:<PR or branch>|publish:<artifact>> <pm> <instruction> [ttl_minutes]"
171
+ nonempty pm "$2"; nonempty instruction "$3"
172
+ row="$(python3 - "$1" "$2" "$3" "${4:-120}" <<'PY'
173
+ import datetime, json, re, secrets, sys
174
+ scope, pm, instruction, ttl = sys.argv[1:5]
175
+ def die(m):
176
+ sys.stderr.write("pmos-log: ERROR — %s\n" % m); sys.exit(1)
177
+ if re.match(r"^(docs-sync|outward)(:|$)", scope):
178
+ die('"%s" is a standing scope. Standing grants are not allowed in this release (D81). Mint one single-use grant per action.' % scope)
179
+ kind, sep, target = scope.partition(":")
180
+ if not sep or kind not in ("merge", "publish"):
181
+ die('unknown scope "%s". Use merge:<PR number or branch> or publish:<artifact>.' % scope)
182
+ pat = r"^(?:[0-9]{1,9}|[A-Za-z0-9][A-Za-z0-9._/-]{0,79})$" if kind == "merge" else r"^[A-Za-z0-9@][A-Za-z0-9@._/-]{0,99}$"
183
+ if not re.match(pat, target) or "*" in target or ".." in target:
184
+ die('"%s" is not a single %s. A grant names one target; a wildcard is refused.' % (target, "PR number or branch name" if kind == "merge" else "artifact name"))
185
+ if not re.match(r"^[0-9]{1,4}$", ttl) or not (1 <= int(ttl) <= 1440):
186
+ die("ttl_minutes must be a whole number from 1 to 1440, got '%s'." % ttl)
187
+ now = datetime.datetime.now(datetime.timezone.utc).replace(microsecond=0)
188
+ fmt = lambda d: d.strftime("%Y-%m-%dT%H:%M:%SZ")
189
+ print(json.dumps({"type": "grant", "id": "g-%d-%s" % (int(now.timestamp()), secrets.token_hex(3)), "scope": kind, "target": target,
190
+ "pm": pm, "at": fmt(now), "expires_at": fmt(now + datetime.timedelta(minutes=int(ttl))), "uses": 1, "instruction": instruction}, separators=(",", ":")))
191
+ PY
192
+ )" || exit 1
193
+ append grants.jsonl "$row"
194
+ ;;
195
+ grant-use)
196
+ [ $# -ge 2 ] || fail "usage: grant-use <grant_id> <action>"
197
+ nonempty grant_id "$1"; nonempty action "$2"
198
+ python3 - "$STATE/grants.jsonl" "$1" <<'PY' || exit 1
199
+ import json, sys
200
+ path, gid = sys.argv[1], sys.argv[2]
201
+ found = used = False
202
+ try:
203
+ lines = open(path, encoding="utf-8").read().splitlines()
204
+ except OSError:
205
+ lines = []
206
+ for l in lines:
207
+ try:
208
+ r = json.loads(l)
209
+ except ValueError:
210
+ continue
211
+ if isinstance(r, dict) and r.get("type") == "grant" and r.get("id") == gid:
212
+ found = True
213
+ if isinstance(r, dict) and r.get("type") == "use" and r.get("grant_id") == gid:
214
+ used = True
215
+ if not found:
216
+ sys.stderr.write("pmos-log: ERROR — grant_id '%s' is not in state/grants.jsonl.\n" % gid); sys.exit(1)
217
+ if used:
218
+ sys.stderr.write("pmos-log: ERROR — grant '%s' was already used. A grant is single use.\n" % gid); sys.exit(1)
219
+ PY
220
+ append grants.jsonl "{\"type\":\"use\",\"grant_id\":$(js "$1"),\"at\":\"$(date -u +%Y-%m-%dT%H:%M:%SZ)\",\"action\":$(js "$2")}"
221
+ ;;
110
222
  *)
111
- fail "unknown command '${cmd:-}' — one of: run | eval | accept | outcome | friction (see AGENTS-HUMAN.md)."
223
+ fail "unknown command '${cmd:-}' — one of: run | eval | accept | outcome | friction | grant | grant-use (see AGENTS-HUMAN.md)."
112
224
  ;;
113
225
  esac
@@ -26,7 +26,7 @@ router is the bug (fix it via the weekly harness scan).
26
26
  1. **Sharpness (the fog/ticket test):** *can the PM state the question or idea precisely now — not
27
27
  answer it, state it?* If not, it is **fog**: not ready for intake. Name what would sharpen it
28
28
  (a conversation, a datum, a spike) and stop — do not open an initiative to hold fog.
29
- 2. **Prior rejection:** check [planning/out-of-scope/](/README.md) for the
29
+ 2. **Prior rejection:** check [planning/out-of-scope/](/planning/out-of-scope/README.md) for the
30
30
  concept. If a file exists, **read it first** and surface it to the PM — the concept returns only
31
31
  if the PM overrides the recorded reasoning, and the file is updated either way.
32
32
  3. **The specimen test — ASK it, do not wait to be told** (D60 as amended 2026-07-24):
@@ -131,7 +131,7 @@ system, a convention set, or a prototype to validate an idea.
131
131
  work has a surface, then runs inside that initiative's Build.
132
132
  - **iterate** → back to step 5 with the PM's reaction folded in.
133
133
  - **kill** → record one honest line on the initiative, **file the concept into
134
- [planning/out-of-scope/](/README.md)** (decision, reasoning, what would
134
+ [planning/out-of-scope/](/planning/out-of-scope/README.md)** (decision, reasoning, what would
135
135
  reopen it) so it is not re-prototyped from scratch, and close. A cheap kill is this lane
136
136
  *succeeding*.
137
137
  8. **Log.** `log_agent_run` with the disposition; friction to discoveries.md (`#friction`).
@@ -49,7 +49,7 @@ volume where informal prioritization strains) are now met.
49
49
 
50
50
  - `planning/briefs/<initiative-id>-brief.md` — operational instance, never in the OKF bundle.
51
51
  - An accepted brief becomes the PRD's parent rationale (the PRD links it). A **no-go** files the
52
- concept into [planning/out-of-scope/](/README.md) so it is not re-litigated.
52
+ concept into [planning/out-of-scope/](/planning/out-of-scope/README.md) so it is not re-litigated.
53
53
 
54
54
  ## Level 3 sub-cases
55
55
 
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: harness-refine
3
- description: >
3
+ description: "Turn PMOS's own run evidence into concrete, evidence-cited edits to the harness. Read the eval defects, recurring friction, run transcripts, and the calibration log; cluster them into specific proposed edits \u2014 each mapped to ONE artifact (an MCP tool description, a skill body, an OKF concept, or an AGENTS.md heuristic) and citing the run/friction/eval that motivates it \u2014 including prunes of scaffolding the model now handles natively. Emit a proposals report and open a PR for the PM. Use on demand: when the PM invokes it, at the D23 quarterly harness review, or when v_friction_recurrence / recurring eval defects show a harness gap. Never merges, never self-approves, never auto-applies."
4
4
  license: MIT
5
5
  metadata:
6
6
  pmos-maturity_mode: active
@@ -28,7 +28,7 @@ router is the bug (fix it via the weekly harness scan).
28
28
  1. **Sharpness (the fog/ticket test):** *can the PM state the question or idea precisely now — not
29
29
  answer it, state it?* If not, it is **fog**: not ready for intake. Name what would sharpen it
30
30
  (a conversation, a datum, a spike) and stop — do not open an initiative to hold fog.
31
- 2. **Prior rejection:** check [planning/out-of-scope/](/README.md) for the
31
+ 2. **Prior rejection:** check [planning/out-of-scope/](/planning/out-of-scope/README.md) for the
32
32
  concept. If a file exists, **read it first** and surface it to the PM — the concept returns only
33
33
  if the PM overrides the recorded reasoning, and the file is updated either way.
34
34
  3. **The specimen test — ASK it, do not wait to be told** (D60 as amended 2026-07-24):
@@ -133,7 +133,7 @@ system, a convention set, or a prototype to validate an idea.
133
133
  work has a surface, then runs inside that initiative's Build.
134
134
  - **iterate** → back to step 5 with the PM's reaction folded in.
135
135
  - **kill** → record one honest line on the initiative, **file the concept into
136
- [planning/out-of-scope/](/README.md)** (decision, reasoning, what would
136
+ [planning/out-of-scope/](/planning/out-of-scope/README.md)** (decision, reasoning, what would
137
137
  reopen it) so it is not re-prototyped from scratch, and close. A cheap kill is this lane
138
138
  *succeeding*.
139
139
  8. **Log.** `log_agent_run` with the disposition; friction to discoveries.md (`#friction`).
@@ -51,7 +51,7 @@ volume where informal prioritization strains) are now met.
51
51
 
52
52
  - `planning/briefs/<initiative-id>-brief.md` — operational instance, never in the OKF bundle.
53
53
  - An accepted brief becomes the PRD's parent rationale (the PRD links it). A **no-go** files the
54
- concept into [planning/out-of-scope/](/README.md) so it is not re-litigated.
54
+ concept into [planning/out-of-scope/](/planning/out-of-scope/README.md) so it is not re-litigated.
55
55
 
56
56
  ## Level 3 sub-cases
57
57
 
@@ -2,7 +2,7 @@
2
2
  type: Reference
3
3
  title: "Discoveries and friction log"
4
4
  description: "Append-only record of what runs discovered and where the process rubbed."
5
- generated: {by: process:create-pmos, at: "2026-10-04T02:41:17Z"}
5
+ generated: {by: process:create-pmos, at: "2026-10-04T08:22:32Z"}
6
6
  status: stable
7
7
  ---
8
8
 
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pmos-workspace",
3
- "version": "0.43.0",
3
+ "version": "0.44.0",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pmos-workspace",
3
- "version": "0.43.0",
3
+ "version": "0.44.0",
4
4
  "private": true,
5
5
  "description": "PMOS Terminal Workspace — local companion hosting real PTYs beside the library and gates (D62)",
6
6
  "license": "MIT",