@zalom/plastic 2.0.0-alpha.9 → 2.0.1

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 (322) hide show
  1. package/PLASTIC.md +13 -136
  2. package/README.md +357 -133
  3. package/agents/plastic-enforcer.md +20 -15
  4. package/agents/plastic-executor.md +15 -4
  5. package/agents/plastic-node-research.md +30 -0
  6. package/agents/plastic-node-verify.md +28 -0
  7. package/agents/plastic-node-work.md +33 -0
  8. package/agents/{plastic-advisor.md → plastic-primary-advisor.md} +7 -9
  9. package/agents/{plastic-faux-advisor.md → plastic-secondary-advisor.md} +9 -12
  10. package/assets/plastic-logo.svg +1 -0
  11. package/bin/crap +4 -0
  12. package/bin/lib/context_budget.rb +43 -6
  13. package/bin/lib/skill_census.rb +839 -0
  14. package/bin/plastic +6 -0
  15. package/bin/plastic-skill-census +114 -0
  16. package/bin/test +24 -4
  17. package/bin/verify-change +345 -0
  18. package/config_asks.yml +4 -4
  19. package/deprecations.yml +1 -1
  20. package/{skills/agent-advisor/references → docs/help}/advisor-protocol.md +19 -24
  21. package/{skills/auto/references → docs/help}/agent-architecture.md +16 -14
  22. package/{skills/conventions/references → docs/help}/completion-and-done.md +15 -16
  23. package/docs/help/human-report-contract.md +152 -0
  24. package/{skills/conventions/references → docs/help}/knowledge-graph.md +9 -0
  25. package/{skills/conventions/references → docs/help}/locks-and-worktrees.md +11 -13
  26. package/{skills/conventions/references → docs/help}/maintenance-and-revisions.md +1 -1
  27. package/{skills/conventions/references → docs/help}/roadmaps.md +2 -2
  28. package/{skills/tutorial/references → docs/help}/track-1-guided.md +28 -47
  29. package/{skills/tutorial/references → docs/help}/track-2-auto.md +8 -8
  30. package/{skills/tutorial/references → docs/help}/track-3-projects-and-roadmaps.md +24 -17
  31. package/hooks/call-budget +4 -0
  32. package/hooks/hooks.json +24 -0
  33. package/hooks/message-display +55 -2
  34. package/hooks/session-start +5 -1
  35. package/hooks/statusline +32 -27
  36. package/hooks/stop +5 -0
  37. package/package.json +5 -3
  38. package/scripts/append-ledger +2 -1
  39. package/scripts/dashboard.rb +267 -16
  40. package/scripts/day-summary +2 -1
  41. package/scripts/doctor.rb +680 -39
  42. package/scripts/end-intent +153 -30
  43. package/scripts/exec-worktree +5 -5
  44. package/scripts/file-session-intent +2 -1
  45. package/scripts/graph-measure +249 -0
  46. package/scripts/hook-call-budget +222 -0
  47. package/scripts/hook-capture +24 -123
  48. package/scripts/hook-close +2 -1
  49. package/scripts/hook-message-display +7 -0
  50. package/scripts/hook-record +24 -16
  51. package/scripts/hook-savepoint +27 -3
  52. package/scripts/hook-session-start +359 -321
  53. package/scripts/hook-stop +58 -0
  54. package/scripts/index-projection +74 -0
  55. package/scripts/insight-append +17 -4
  56. package/scripts/install.rb +9 -7
  57. package/scripts/lib/action_graph_shim.rb +279 -0
  58. package/scripts/lib/active_delivery.rb +105 -0
  59. package/scripts/lib/agent_models.rb +63 -25
  60. package/scripts/lib/arm.rb +48 -83
  61. package/scripts/lib/atomic_write.rb +31 -0
  62. package/scripts/lib/backup.rb +65 -0
  63. package/scripts/lib/cli/command.rb +92 -0
  64. package/scripts/lib/cli/commands/auto.rb +18 -0
  65. package/scripts/lib/cli/commands/auto_brief.rb +44 -0
  66. package/scripts/lib/cli/commands/auto_lock.rb +99 -0
  67. package/scripts/lib/cli/commands/auto_report.rb +50 -0
  68. package/scripts/lib/cli/commands/auto_take.rb +31 -0
  69. package/scripts/lib/cli/commands/backup.rb +43 -0
  70. package/scripts/lib/cli/commands/checkout.rb +25 -0
  71. package/scripts/lib/cli/commands/continue.rb +66 -0
  72. package/scripts/lib/cli/commands/doctor.rb +81 -0
  73. package/scripts/lib/cli/commands/feedback.rb +40 -0
  74. package/scripts/lib/cli/commands/help.rb +69 -0
  75. package/scripts/lib/cli/commands/hook.rb +32 -0
  76. package/scripts/lib/cli/commands/index.rb +23 -0
  77. package/scripts/lib/cli/commands/install.rb +21 -0
  78. package/scripts/lib/cli/commands/installer_verb.rb +37 -0
  79. package/scripts/lib/cli/commands/intent.rb +19 -0
  80. package/scripts/lib/cli/commands/intent_answer.rb +37 -0
  81. package/scripts/lib/cli/commands/intent_command.rb +53 -0
  82. package/scripts/lib/cli/commands/intent_end.rb +62 -0
  83. package/scripts/lib/cli/commands/intent_new.rb +62 -0
  84. package/scripts/lib/cli/commands/intent_note.rb +43 -0
  85. package/scripts/lib/cli/commands/intent_rule.rb +36 -0
  86. package/scripts/lib/cli/commands/intent_show.rb +32 -0
  87. package/scripts/lib/cli/commands/intent_spec.rb +44 -0
  88. package/scripts/lib/cli/commands/intent_step.rb +71 -0
  89. package/scripts/lib/cli/commands/intent_verify.rb +26 -0
  90. package/scripts/lib/cli/commands/migrate.rb +16 -0
  91. package/scripts/lib/cli/commands/migrate_stores.rb +31 -0
  92. package/scripts/lib/cli/commands/next.rb +54 -0
  93. package/scripts/lib/cli/commands/project.rb +19 -0
  94. package/scripts/lib/cli/commands/project_links.rb +42 -0
  95. package/scripts/lib/cli/commands/project_list.rb +20 -0
  96. package/scripts/lib/cli/commands/project_new.rb +72 -0
  97. package/scripts/lib/cli/commands/query.rb +31 -0
  98. package/scripts/lib/cli/commands/render.rb +33 -0
  99. package/scripts/lib/cli/commands/roadmap.rb +19 -0
  100. package/scripts/lib/cli/commands/roadmap_check.rb +54 -0
  101. package/scripts/lib/cli/commands/roadmap_log.rb +54 -0
  102. package/scripts/lib/cli/commands/roadmap_migrate.rb +53 -0
  103. package/scripts/lib/cli/commands/roadmap_next.rb +70 -0
  104. package/scripts/lib/cli/commands/roadmap_show.rb +45 -0
  105. package/scripts/lib/cli/commands/rollback.rb +20 -0
  106. package/scripts/lib/cli/commands/search.rb +60 -0
  107. package/scripts/lib/cli/commands/session.rb +18 -0
  108. package/scripts/lib/cli/commands/session_commit.rb +42 -0
  109. package/scripts/lib/cli/commands/session_handoff.rb +34 -0
  110. package/scripts/lib/cli/commands/session_summary.rb +35 -0
  111. package/scripts/lib/cli/commands/status.rb +68 -0
  112. package/scripts/lib/cli/commands/subcommand_list.rb +36 -0
  113. package/scripts/lib/cli/commands/sync.rb +46 -0
  114. package/scripts/lib/cli/commands/uninstall.rb +20 -0
  115. package/scripts/lib/cli/commands/update.rb +20 -0
  116. package/scripts/lib/cli/commands/version.rb +53 -0
  117. package/scripts/lib/cli/frontier.rb +86 -0
  118. package/scripts/lib/cli/intent_progress.rb +36 -0
  119. package/scripts/lib/cli/legacy.rb +80 -0
  120. package/scripts/lib/cli/output.rb +137 -0
  121. package/scripts/lib/cli/scope.rb +134 -0
  122. package/scripts/lib/cli/table.rb +65 -0
  123. package/scripts/lib/cli.rb +94 -0
  124. package/scripts/lib/codex_adapter.rb +198 -0
  125. package/scripts/lib/compact_instructions.rb +13 -5
  126. package/scripts/lib/core_integrity.rb +71 -0
  127. package/scripts/lib/dashboard_screen.rb +40 -0
  128. package/scripts/lib/data_boundary.rb +132 -0
  129. package/scripts/lib/day_summary.rb +23 -14
  130. package/scripts/lib/doctor_core.rb +114 -38
  131. package/scripts/lib/doctor_session_ledger.rb +5 -53
  132. package/scripts/lib/engine_permissions.rb +88 -0
  133. package/scripts/lib/exec_worktree.rb +24 -21
  134. package/scripts/lib/feedback_report.rb +1 -1
  135. package/scripts/lib/graph_edges.rb +137 -0
  136. package/scripts/lib/graph_file.rb +246 -0
  137. package/scripts/lib/graph_measure.rb +645 -0
  138. package/scripts/lib/graph_measure_budget.rb +409 -0
  139. package/scripts/lib/graph_measure_cohorts.rb +487 -0
  140. package/scripts/lib/graph_measure_models.rb +413 -0
  141. package/scripts/lib/graph_measure_report.rb +532 -0
  142. package/scripts/lib/graph_tree.rb +98 -0
  143. package/scripts/lib/guarded_append.rb +155 -0
  144. package/scripts/lib/handoff.rb +40 -13
  145. package/scripts/lib/harness_adapter.rb +184 -0
  146. package/scripts/lib/hook_registry.rb +27 -3
  147. package/scripts/lib/hook_replay.rb +229 -0
  148. package/scripts/lib/index_entry.rb +62 -0
  149. package/scripts/lib/index_projection.rb +201 -0
  150. package/scripts/lib/insights.rb +1 -1
  151. package/scripts/lib/installer_core.rb +531 -71
  152. package/scripts/lib/intent_screen.rb +4 -4
  153. package/scripts/lib/intent_screen_ansi.rb +73 -12
  154. package/scripts/lib/intent_validator.rb +2 -2
  155. package/scripts/lib/lock.rb +10 -11
  156. package/scripts/lib/message_display.rb +350 -54
  157. package/scripts/lib/meter_watch.rb +185 -0
  158. package/scripts/lib/node_file.rb +234 -0
  159. package/scripts/lib/node_ids.rb +99 -0
  160. package/scripts/lib/node_input.rb +913 -0
  161. package/scripts/lib/node_input_compatibility.rb +62 -0
  162. package/scripts/lib/node_ledger.rb +386 -0
  163. package/scripts/lib/node_progress.rb +153 -0
  164. package/scripts/lib/node_return.rb +204 -0
  165. package/scripts/lib/node_worktree.rb +337 -0
  166. package/scripts/lib/outcome_report.rb +440 -0
  167. package/scripts/lib/preflight.rb +4 -6
  168. package/scripts/lib/project_config.rb +46 -0
  169. package/scripts/lib/project_validator.rb +3 -2
  170. package/scripts/lib/qmd_sync.rb +8 -7
  171. package/scripts/lib/ready_set.rb +462 -0
  172. package/scripts/lib/reference_archive.rb +45 -0
  173. package/scripts/lib/release_guard.rb +18 -0
  174. package/scripts/lib/report_screen.rb +1371 -47
  175. package/scripts/lib/rlm/corpus.rb +13 -0
  176. package/scripts/lib/rlm/probe.rb +29 -0
  177. package/scripts/lib/rlm/query.rb +22 -0
  178. package/scripts/lib/roadmap_graph.rb +210 -0
  179. package/scripts/lib/roadmap_migration.rb +95 -0
  180. package/scripts/lib/roadmap_queue.rb +158 -8
  181. package/scripts/lib/roadmap_render.rb +150 -0
  182. package/scripts/lib/roadmap_savepoint.rb +64 -14
  183. package/scripts/lib/runner_absorb.rb +703 -0
  184. package/scripts/lib/runner_answer.rb +206 -0
  185. package/scripts/lib/runner_core.rb +194 -0
  186. package/scripts/lib/runner_dispatch.rb +525 -0
  187. package/scripts/lib/runner_policy.rb +191 -0
  188. package/scripts/lib/runner_proposals.rb +275 -0
  189. package/scripts/lib/runner_rewind.rb +201 -0
  190. package/scripts/lib/runner_sweep.rb +231 -0
  191. package/scripts/lib/runner_until_empty.rb +252 -0
  192. package/scripts/lib/runner_watch.rb +389 -0
  193. package/scripts/lib/savepoint.rb +141 -19
  194. package/scripts/lib/scaffold_intent.rb +6 -3
  195. package/scripts/lib/screen_paint.rb +365 -28
  196. package/scripts/lib/screens/dashboard.rb +20 -0
  197. package/scripts/lib/screens/plan.rb +18 -0
  198. package/scripts/lib/screens/roadmap.rb +15 -0
  199. package/scripts/lib/search_index.rb +55 -0
  200. package/scripts/lib/session_close.rb +30 -28
  201. package/scripts/lib/session_git.rb +25 -18
  202. package/scripts/lib/session_ledger.rb +48 -4
  203. package/scripts/lib/session_usage.rb +190 -0
  204. package/scripts/lib/sqlite.rb +22 -0
  205. package/scripts/lib/stop_gate.rb +95 -0
  206. package/scripts/lib/store_discovery.rb +7 -6
  207. package/scripts/lib/store_layout.rb +54 -0
  208. package/scripts/lib/store_provisioning.rb +2 -1
  209. package/scripts/lib/store_sync.rb +85 -0
  210. package/scripts/lib/stores_move.rb +93 -0
  211. package/scripts/lib/verify_intent.rb +36 -8
  212. package/scripts/lib/version_number.rb +48 -0
  213. package/scripts/lib/work_graph.rb +59 -0
  214. package/scripts/lib/work_graph_validator.rb +201 -0
  215. package/scripts/lib/worktree.rb +27 -32
  216. package/scripts/lib/worktree_sweep.rb +6 -5
  217. package/scripts/link-suggest +2 -1
  218. package/scripts/meter-watch +57 -0
  219. package/scripts/migrate-to-global +1 -1
  220. package/scripts/new-intent +4 -13
  221. package/scripts/node-input +92 -0
  222. package/scripts/node-run +225 -0
  223. package/scripts/node-transition +291 -0
  224. package/scripts/outcome-report +74 -0
  225. package/scripts/plastic-lock +42 -33
  226. package/scripts/promote-session-item +3 -2
  227. package/scripts/read-config +53 -9
  228. package/scripts/ready-set +126 -0
  229. package/scripts/release-check +123 -0
  230. package/scripts/report-screen +177 -16
  231. package/scripts/roadmap-graph +119 -0
  232. package/scripts/roadmap-savepoint +7 -0
  233. package/scripts/runner +581 -0
  234. package/scripts/savepoint-note +11 -9
  235. package/scripts/session-commit +2 -1
  236. package/scripts/session-usage +56 -0
  237. package/scripts/skill-lint +115 -6
  238. package/scripts/spawn-preamble +2 -2
  239. package/scripts/update.rb +25 -4
  240. package/scripts/validate-work-graph +39 -0
  241. package/scripts/verify-intent +3 -2
  242. package/scripts/write-handoff +2 -1
  243. package/templates/agents.md +7 -7
  244. package/templates/config.yml +16 -9
  245. package/templates/dashboard-screen.md +22 -0
  246. package/templates/display-fixture.md +21 -0
  247. package/templates/graph.md +16 -0
  248. package/templates/index.md +1 -1
  249. package/templates/intent-screen.md +1 -1
  250. package/templates/node-decision.md +11 -0
  251. package/templates/node-research.md +13 -0
  252. package/templates/node-verify.md +13 -0
  253. package/templates/node-work.md +22 -0
  254. package/templates/outcome.md +8 -3
  255. package/templates/project.yml +1 -1
  256. package/templates/render.css +10 -0
  257. package/templates/report-plan.md +15 -0
  258. package/templates/report-roadmap-delivered.md +10 -0
  259. package/templates/report-roadmap-plan.md +9 -0
  260. package/templates/report-roadmap-state.md +9 -0
  261. package/templates/report-state.md +1 -1
  262. package/templates/roadmap.md +13 -0
  263. package/bin/plastic.js +0 -70
  264. package/scripts/lib/bridge.rb +0 -116
  265. package/skills/agent-advisor/SKILL.md +0 -92
  266. package/skills/auto/SKILL.md +0 -296
  267. package/skills/auto/evals/evals.json +0 -255
  268. package/skills/auto/references/end-tail.md +0 -66
  269. package/skills/auto/references/human-report-contract.md +0 -78
  270. package/skills/conventions/SKILL.md +0 -29
  271. package/skills/dashboard/SKILL.md +0 -169
  272. package/skills/dashboard/evals/evals.json +0 -38
  273. package/skills/dashboard/references/classification.md +0 -22
  274. package/skills/dashboard/templates/dashboard-global.md +0 -20
  275. package/skills/dashboard/templates/dashboard-project.md +0 -19
  276. package/skills/direct/SKILL.md +0 -66
  277. package/skills/direct/references/request-signals.md +0 -59
  278. package/skills/doctor/SKILL.md +0 -299
  279. package/skills/doctor/report.md +0 -102
  280. package/skills/feedback/SKILL.md +0 -98
  281. package/skills/feedback/references/transport-and-privacy.md +0 -65
  282. package/skills/feedback/report.md +0 -36
  283. package/skills/install/SKILL.md +0 -217
  284. package/skills/intent-continuing/SKILL.md +0 -154
  285. package/skills/intent-continuing/references/board-fill.md +0 -43
  286. package/skills/intent-continuing/references/boarding-matrix.md +0 -34
  287. package/skills/intent-continuing/references/context-management.md +0 -28
  288. package/skills/intent-continuing/references/liveness-ranking.md +0 -57
  289. package/skills/intent-creating/SKILL.md +0 -164
  290. package/skills/intent-creating/evals/evals.json +0 -72
  291. package/skills/intent-creating/references/lifecycle.md +0 -81
  292. package/skills/intent-creating/references/wikilinks.md +0 -8
  293. package/skills/intent-ending/SKILL.md +0 -176
  294. package/skills/intent-ending/evals/evals.json +0 -74
  295. package/skills/intent-executing/SKILL.md +0 -170
  296. package/skills/intent-executing/evals/evals.json +0 -66
  297. package/skills/intent-executing/implementer-prompt.md +0 -42
  298. package/skills/intent-executing/spec-reviewer-prompt.md +0 -27
  299. package/skills/intent-speccing/SKILL.md +0 -130
  300. package/skills/intent-speccing/evals/evals.json +0 -126
  301. package/skills/intent-speccing/references/design-principles.md +0 -44
  302. package/skills/intent-speccing/references/per-section-fill-rules.md +0 -92
  303. package/skills/intent-speccing/references/self-verify-checklist.md +0 -37
  304. package/skills/project-creating/SKILL.md +0 -162
  305. package/skills/project-creating/references/hubs-projects.md +0 -55
  306. package/skills/project-creating/references/project-scaffolding.md +0 -97
  307. package/skills/releasing/SKILL.md +0 -337
  308. package/skills/releasing/references/deprecations.md +0 -60
  309. package/skills/releasing/references/promotion-and-tagging.md +0 -66
  310. package/skills/releasing/references/release-lines.md +0 -105
  311. package/skills/roadmap/SKILL.md +0 -64
  312. package/skills/roadmap/references/file-format.md +0 -124
  313. package/skills/roadmap/references/operations.md +0 -112
  314. package/skills/rollback/SKILL.md +0 -91
  315. package/skills/tutorial/SKILL.md +0 -65
  316. package/skills/tutorial/evals/evals.json +0 -186
  317. package/skills/uninstall/SKILL.md +0 -75
  318. package/skills/update/SKILL.md +0 -126
  319. /package/{skills/auto/references → docs/help}/agent-report-contract.md +0 -0
  320. /package/{skills/intent-executing → docs/help}/code-quality-reviewer-prompt.md +0 -0
  321. /package/{skills/conventions/references → docs/help}/lifecycle-and-savepoints.md +0 -0
  322. /package/{skills/intent-executing → docs/help}/plan-reviewer-prompt.md +0 -0
package/bin/plastic ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env -S ruby --disable-gems
2
+ # frozen_string_literal: true
3
+
4
+ require_relative "../scripts/lib/cli"
5
+
6
+ exit Plastic::CLI.call(ARGV)
@@ -0,0 +1,114 @@
1
+ #!/usr/bin/env ruby
2
+ # encoding: UTF-8
3
+ # frozen_string_literal: true
4
+
5
+ # Plastic skill usage census (intent 324). Counts typed /plastic-* commands
6
+ # from the prompt history against agent-invoked Skill calls, loads, and
7
+ # attribution from the transcript tree, so D19 (which Plastic verbs stay
8
+ # visible as slash commands) is decided on measured use.
9
+ #
10
+ # Maintainer tool: it reads the paths given to it and is never installed
11
+ # into ~/.plastic. This file never resolves the running process's home
12
+ # directory or its home environment variable: `bin/` ships in the npm
13
+ # tarball, so a default rooted there would make a published executable
14
+ # read any installer's transcripts. `--history` and `--transcripts` are
15
+ # required, with no default.
16
+ #
17
+ # Usage: bin/plastic-skill-census --history PATH --transcripts DIR
18
+ # [--skills-dir DIR] [--format md|json] [--out PATH]
19
+ # [--cutoff YYYY-MM-DD]
20
+ #
21
+ # --history PATH ~/.claude/history.jsonl, or a fixture (required)
22
+ # --transcripts DIR ~/.claude/projects, or a fixture tree (required)
23
+ # --skills-dir DIR a skill roster directory (default: this repo's skills/)
24
+ # --format md|json report format (default: md)
25
+ # --out PATH write the report here instead of stdout
26
+ # --cutoff YYYY-MM-DD exclude records on or after this date (default: 2026-09-02)
27
+ #
28
+ # Exit codes: 0 a report was produced, 1 a named path does not exist,
29
+ # 2 bad usage or a missing required flag.
30
+
31
+ require_relative "lib/skill_census"
32
+
33
+ USAGE = <<~TEXT
34
+ usage: plastic-skill-census --history PATH --transcripts DIR [--skills-dir DIR] [--format md|json] [--out PATH] [--cutoff YYYY-MM-DD]
35
+
36
+ --history PATH ~/.claude/history.jsonl, or a fixture (required)
37
+ --transcripts DIR ~/.claude/projects, or a fixture tree (required)
38
+ --skills-dir DIR a skill roster directory (default: this repo's skills/)
39
+ --format md|json report format (default: md)
40
+ --out PATH write the report here instead of stdout
41
+ --cutoff YYYY-MM-DD exclude records on or after this date (default: #{SkillCensus::DEFAULT_CUTOFF})
42
+ --help this message
43
+
44
+ Exits 0 on a report, 1 when a named path is missing, 2 on bad usage.
45
+ TEXT
46
+
47
+ def fail_usage(message)
48
+ warn "plastic-skill-census: #{message}"
49
+ warn USAGE
50
+ exit 2
51
+ end
52
+
53
+ history = nil
54
+ transcripts = nil
55
+ skills_dir = File.expand_path("../skills", __dir__)
56
+ format = "md"
57
+ out_path = nil
58
+ cutoff = SkillCensus::DEFAULT_CUTOFF
59
+
60
+ argv = ARGV.dup
61
+ until argv.empty?
62
+ case (flag = argv.shift)
63
+ when "--help", "-h"
64
+ puts USAGE
65
+ exit 0
66
+ when "--history"
67
+ history = argv.shift
68
+ fail_usage("--history needs a path") if history.to_s.empty?
69
+ when "--transcripts"
70
+ transcripts = argv.shift
71
+ fail_usage("--transcripts needs a path") if transcripts.to_s.empty?
72
+ when "--skills-dir"
73
+ skills_dir = argv.shift
74
+ fail_usage("--skills-dir needs a path") if skills_dir.to_s.empty?
75
+ when "--format"
76
+ format = argv.shift
77
+ fail_usage("--format must be md or json") unless %w[md json].include?(format)
78
+ when "--out"
79
+ out_path = argv.shift
80
+ fail_usage("--out needs a path") if out_path.to_s.empty?
81
+ when "--cutoff"
82
+ cutoff = argv.shift
83
+ fail_usage("--cutoff needs a YYYY-MM-DD date") if cutoff.to_s.empty?
84
+ else
85
+ fail_usage("unknown argument #{flag}")
86
+ end
87
+ end
88
+
89
+ fail_usage("--history is required") if history.nil?
90
+ fail_usage("--transcripts is required") if transcripts.nil?
91
+
92
+ begin
93
+ roster = SkillCensus::Roster.load(skills_dir)
94
+ history_scan = SkillCensus::HistoryScanner.new(history, cutoff: cutoff).scan
95
+ transcript_scan = SkillCensus::TranscriptScanner.new(transcripts, cutoff: cutoff).scan
96
+ built = SkillCensus::Tally.new(history_scan, transcript_scan, roster).build
97
+
98
+ report = if format == "json"
99
+ SkillCensus::Report.json(built, history_path: history, transcripts_path: transcripts, cutoff: cutoff)
100
+ else
101
+ SkillCensus::Report.markdown(built, history_path: history, transcripts_path: transcripts, cutoff: cutoff)
102
+ end
103
+ rescue RuntimeError => error
104
+ warn "plastic-skill-census: #{error.message}"
105
+ exit 1
106
+ end
107
+
108
+ if out_path
109
+ File.write(out_path, report)
110
+ else
111
+ puts report
112
+ end
113
+
114
+ exit 0
package/bin/test CHANGED
@@ -2,7 +2,10 @@
2
2
  # encoding: UTF-8
3
3
  # frozen_string_literal: true
4
4
 
5
- # Runs the FULL Minitest suite in a single process.
5
+ # Runs the Minitest suite in a single process, either the whole suite or a
6
+ # named subset via --only (intent 355, n4, D5). Every run, named or full,
7
+ # goes through FailuresReporter, so a green run costs one line and a red run
8
+ # prints only the failures plus that line, never a dot per test.
6
9
  #
7
10
  # `release.verify` used to be `ruby -Itest test/*_test.rb`, but Ruby runs only
8
11
  # the first glob-expanded file as the program — the rest land in ARGV and are
@@ -12,17 +15,34 @@
12
15
  # loaded.
13
16
 
14
17
  root = File.expand_path("..", __dir__)
15
-
16
- files = Dir.glob(File.join(root, "test", "**", "*_test.rb")).sort
17
- abort "No test files found under #{File.join(root, "test")}" if files.empty?
18
+ discover = -> { Dir.glob(File.join(root, "test", "**", "*_test.rb")).sort }
18
19
 
19
20
  # `bin/test --list` prints the discovered files without running them, so the
20
21
  # discovery logic can be tested cheaply (no full-suite execution).
21
22
  if ARGV.delete("--list")
23
+ files = discover.call
24
+ abort "No test files found under #{File.join(root, "test")}" if files.empty?
22
25
  puts files.map { |f| f.sub("#{root}/", "") }
23
26
  exit 0
24
27
  end
25
28
 
29
+ only_index = ARGV.index("--only")
30
+ if only_index
31
+ named = ARGV.slice!(only_index..-1)
32
+ named.shift
33
+ abort "--only requires at least one test file" if named.empty?
34
+ files = named.map { |f| File.expand_path(f, root) }
35
+ missing = files.reject { |f| File.exist?(f) }
36
+ unless missing.empty?
37
+ warn "no such test file: #{missing.map { |f| f.sub("#{root}/", "") }.join(', ')}"
38
+ exit 2
39
+ end
40
+ else
41
+ files = discover.call
42
+ abort "No test files found under #{File.join(root, "test")}" if files.empty?
43
+ end
44
+
26
45
  $LOAD_PATH.unshift File.join(root, "test")
46
+ require_relative "../test/lib/failures_reporter"
27
47
  require "minitest/autorun"
28
48
  files.each { |f| require f }
@@ -0,0 +1,345 @@
1
+ #!/usr/bin/env ruby
2
+ require "fileutils"
3
+ require "open3"
4
+ require "tmpdir"
5
+
6
+ class VerifyChange
7
+ USAGE = <<~TEXT
8
+ Usage: bin/verify-change [BASE] [--test FILE]...
9
+
10
+ Checks only the code changed since the merge base of BASE and HEAD
11
+ (BASE defaults to origin/main, then main, then master):
12
+
13
+ 1. lint on the changed Ruby files, in the project's style guide
14
+ 2. the tests for the changed files, plus every --test FILE, with coverage on
15
+ 3. patch coverage of the changed lines (simplecov patch)
16
+ 4. mutation testing of the changed lines (mutineer, operators from .mutineer.yml)
17
+ 5. CRAP scores of the changed methods (bin/crap)
18
+
19
+ Runs RSpec specs in a project with spec/ and .rspec, spec/spec_helper.rb,
20
+ or spec/rails_helper.rb, or with rspec, rspec-core, or rspec-rails under
21
+ DEPENDENCIES in Gemfile.lock, and Minitest tests otherwise. Warns when
22
+ both test/ and spec/ exist. Pass --test for the integration or system
23
+ tests of a feature the change touches.
24
+
25
+ A lint failure is reported but does not stop the other checks. Lint is
26
+ skipped, not failed, with no style guide, two style guides, no changed
27
+ Ruby files, or the linter gem missing from Gemfile.lock.
28
+
29
+ Every child runs under a throwaway HOME and PLASTIC_TMP, so a test or a
30
+ mutant that spawns an installer cannot reach the real home directory.
31
+
32
+ Exit codes: 0 verified, 1 a check failed, 2 usage or git error.
33
+ TEXT
34
+
35
+ PATCH_COVERAGE_MINIMUM = 100
36
+ MUTATION_THRESHOLD = 75
37
+ CRAP_THRESHOLD = 30
38
+ TESTS_STEP = "Tests with coverage"
39
+ LINT_STEP = "Lint"
40
+ DEFAULT_BASES = %w[origin/main main master].freeze
41
+ ALL_OPERATORS = %w[arithmetic comparison boolean_connector boolean_literal statement_removal return_nil
42
+ literal_mutation condition_negation string_literal regex collection_method].freeze
43
+ SOURCE_PATH = %r{\A(app|lib|tools|scripts|bin/lib)/.+\.rb\z}
44
+ RSPEC_DEPENDENCY = /^ rspec(-core|-rails)?(\s|!|$)/
45
+
46
+ Suite = Data.define(:name, :folder, :test_path, :locations, :suffix)
47
+ MINITEST = Suite.new(name: "Minitest", folder: "test/", test_path: %r{\Atest/.+_test\.rb\z}, suffix: "_test.rb",
48
+ locations: { %r{\Aapp/} => ["test/"], %r{\Alib/} => ["test/", "test/lib/"], %r{\Atools/} => ["test/tools/"],
49
+ %r{\Ascripts/lib/} => ["test/", "test/lib/"], %r{\Abin/lib/} => ["test/"], %r{\Ascripts/} => ["test/"] })
50
+ RSPEC = Suite.new(name: "RSpec", folder: "spec/", test_path: %r{\Aspec/.+_spec\.rb\z}, suffix: "_spec.rb",
51
+ locations: { %r{\Aapp/} => ["spec/"], %r{\Alib/} => ["spec/", "spec/lib/"], %r{\Atools/} => ["spec/tools/"] })
52
+
53
+ Step = Struct.new(:title, :env, :command)
54
+ LintDecision = Struct.new(:step, :skip_reason)
55
+ Plan = Struct.new(:sources, :tests, :untested, :lint_files, :lint_decision, :steps)
56
+
57
+ def self.test_candidates(source, suite = MINITEST)
58
+ suite.locations.flat_map do |prefix, targets|
59
+ source.match?(prefix) ? targets.map { |target| source.sub(prefix, target).sub(/\.rb\z/, suite.suffix) } : []
60
+ end
61
+ end
62
+
63
+ def initialize(argv, root: Dir.pwd, out: $stdout, err: $stderr, git: nil, run: nil, sandbox: nil)
64
+ @argv = argv.dup
65
+ @root = root
66
+ @out = out
67
+ @err = err
68
+ @git = git || ->(*args) { Open3.capture2e("git", "-C", root, *args) }
69
+ @run = run || ->(env, command) { system(env, *command, chdir: root) }
70
+ @sandbox = sandbox
71
+ @owned_sandbox = sandbox.nil?
72
+ end
73
+
74
+ # A throwaway home for every child this script spawns. A gate step runs the
75
+ # project's own tests, and a test (or a mutant that defeats an injected
76
+ # runner) can spawn an installer, which resolves ~/.plastic and ~/.claude
77
+ # from HOME. Pointing HOME at a fresh directory is what keeps the real home
78
+ # out of reach; PLASTIC_TMP does the same for the session bridge files.
79
+ def sandbox
80
+ @sandbox ||= { "HOME" => Dir.mktmpdir("verify-change-home"),
81
+ "PLASTIC_TMP" => Dir.mktmpdir("verify-change-tmp") }
82
+ end
83
+
84
+ def call
85
+ return usage(0) if @argv.include?("--help") || @argv.include?("-h")
86
+
87
+ requested_base, extra_tests = parse_arguments
88
+ return usage(2) unless requested_base
89
+
90
+ base = merge_base(requested_base)
91
+ return 2 unless base
92
+
93
+ report_untracked
94
+ chosen = suite
95
+ report_both_suites(chosen)
96
+ plan = plan(changed: changed_files(base), extra_tests: extra_tests, base: base, rails: rails?, suite: chosen)
97
+ execute(plan, requested_base)
98
+ ensure
99
+ discard_sandbox
100
+ end
101
+
102
+ def discard_sandbox
103
+ return unless @owned_sandbox && @sandbox
104
+
105
+ @sandbox.each_value { |dir| FileUtils.remove_entry(dir, true) }
106
+ @sandbox = nil
107
+ end
108
+
109
+ def plan(changed:, extra_tests:, base:, rails:, suite: MINITEST)
110
+ sources = changed.grep(SOURCE_PATH).select { |path| exist?(path) }
111
+ lint_files = changed.select { |path| File.extname(path) == ".rb" && exist?(path) }
112
+ mapped = sources.to_h { |source| [source, self.class.test_candidates(source, suite).select { |path| exist?(path) }] }
113
+ changed_tests = changed.grep(suite.test_path)
114
+ tests = (changed_tests + mapped.values.flatten + extra_tests).uniq.select { |path| exist?(path) }
115
+ covered_elsewhere = !extra_tests.empty? || changed_tests.any?
116
+ untested = covered_elsewhere ? [] : mapped.select { |_source, found| found.empty? }.keys
117
+ Plan.new(sources, tests, untested, lint_files, lint_decision(lint_files), steps(sources, tests, base, rails, suite))
118
+ end
119
+
120
+ private
121
+
122
+ def parse_arguments
123
+ base = nil
124
+ extra_tests = []
125
+ arguments = @argv.dup
126
+ until arguments.empty?
127
+ argument = arguments.shift
128
+ if argument == "--test"
129
+ file = arguments.shift
130
+ return [nil, []] unless file
131
+
132
+ extra_tests << file
133
+ elsif argument.start_with?("-") || base
134
+ return [nil, []]
135
+ else
136
+ base = argument
137
+ end
138
+ end
139
+ [base || default_base, extra_tests]
140
+ end
141
+
142
+ def default_base
143
+ DEFAULT_BASES.find { |ref| @git.call("rev-parse", "--verify", "--quiet", ref).last.success? } || DEFAULT_BASES.last
144
+ end
145
+
146
+ def merge_base(ref)
147
+ output, status = @git.call("merge-base", ref, "HEAD")
148
+ return output.strip if status.success?
149
+
150
+ @err.puts "Cannot find the merge base of #{ref} and HEAD: #{output.strip}"
151
+ nil
152
+ end
153
+
154
+ def changed_files(base)
155
+ @git.call("diff", "--name-only", base, "--", "*.rb").first.split("\n")
156
+ end
157
+
158
+ def report_untracked
159
+ untracked = @git.call("ls-files", "--others", "--exclude-standard", "--", "*.rb").first.split("\n")
160
+ return if untracked.empty?
161
+
162
+ @err.puts "Not checked, because git does not track them yet (run git add first): #{untracked.join(", ")}"
163
+ end
164
+
165
+ def report_both_suites(chosen)
166
+ return unless File.directory?(File.join(@root, "test")) && File.directory?(File.join(@root, "spec"))
167
+
168
+ skipped = chosen == RSPEC ? MINITEST : RSPEC
169
+ @err.puts "Both test/ and spec/ exist. Checking with #{chosen.name} only, so changed files under #{skipped.folder} do not run."
170
+ end
171
+
172
+ def steps(sources, tests, base, rails, suite)
173
+ steps = []
174
+ steps << step(TESTS_STEP, { "COVERAGE" => "1" }, test_command(tests, rails, suite)) unless tests.empty?
175
+ return steps if sources.empty?
176
+
177
+ steps << step("Patch coverage", {}, %W[bundle exec simplecov patch --base #{base} --minimum #{PATCH_COVERAGE_MINIMUM}])
178
+ steps << step("Mutation testing", {}, mutation_command(sources, tests, base, rails, suite))
179
+ steps << step("CRAP scores", {}, %W[bundle exec bin/crap --threshold #{CRAP_THRESHOLD} --since #{base}] + sources)
180
+ end
181
+
182
+ def step(title, env, command)
183
+ Step.new(title, sandbox.merge(env), command)
184
+ end
185
+
186
+ def test_command(tests, rails, suite)
187
+ return %w[bundle exec rspec] + tests if suite == RSPEC
188
+ return %w[bin/rails test] + tests if rails
189
+
190
+ %w[bundle exec ruby -Ilib -Itools -Itest -e] + [%(require "test_helper"; ARGV.each { |file| require File.expand_path(file) })] + tests
191
+ end
192
+
193
+ def lint_decision(lint_files)
194
+ return LintDecision.new(nil, "no changed Ruby files to lint") if lint_files.empty?
195
+ return LintDecision.new(nil, "two style guides found: .standard.yml and .rubocop.yml") if ambiguous_style_guide?
196
+
197
+ if rubocop_file?
198
+ return lint_gem_available?(:rubocop) ? LintDecision.new(lint_step(:rubocop, lint_files), nil) : LintDecision.new(nil, "the rubocop gem is not in Gemfile.lock")
199
+ end
200
+
201
+ if standard_file? || dependencies_section.match?(/^ standard(\s|!|$)/)
202
+ return lint_gem_available?(:standard) ? LintDecision.new(lint_step(:standard, lint_files), nil) : LintDecision.new(nil, "the standard gem is not in Gemfile.lock")
203
+ end
204
+
205
+ LintDecision.new(nil, "no style guide found")
206
+ end
207
+
208
+ # Local adaptation (intent 363): RuboCop reads .rubocop_with_todo.yml here, which
209
+ # is .rubocop.yml plus the generated todo. The todo holds this repository's 16,116
210
+ # pre-existing offences, so a lint run reports what the change did and nothing
211
+ # else. .rubocop.yml, the todo-free config, is what the repository is measured
212
+ # against as the todo shrinks.
213
+ def lint_step(guide, lint_files)
214
+ command = guide == :standard ? %w[bundle exec standardrb --force-exclusion] : rubocop_command
215
+ step(LINT_STEP, {}, command + lint_files)
216
+ end
217
+
218
+ def rubocop_command
219
+ base = %w[bundle exec rubocop --force-exclusion]
220
+ exist?(".rubocop_with_todo.yml") ? base + %w[--config .rubocop_with_todo.yml] : base
221
+ end
222
+
223
+ def lint_gem_available?(command_gem)
224
+ return true if command_gem == :rubocop && (dependencies_section.match?(/^ rubocop(\s|!|$)/) ||
225
+ dependencies_section.match?(/^ standard(\s|!|$)/) || dependencies_section.match?(/^ rubocop-rails-omakase(\s|!|$)/))
226
+ return true if command_gem == :standard && dependencies_section.match?(/^ standard(\s|!|$)/)
227
+
228
+ false
229
+ end
230
+
231
+ def ambiguous_style_guide?
232
+ standard_file? && rubocop_file? && !rubocop_config_uses_standard?
233
+ end
234
+
235
+ def rubocop_config_uses_standard?
236
+ config = rubocop_config
237
+ return false if config.empty?
238
+
239
+ config.match?(/inherit_gem:\s*\{?\s*standard:/m) ||
240
+ config.match?(/^(plugins|require):\s*\n(?:[ \t]*-[ \t]*\S+[ \t]*\n)*?[ \t]*-[ \t]*standard[ \t]*$/m) ||
241
+ config.match?(/^(plugins|require):\s*\[[^\]]*\bstandard\b[^\]]*\]/m)
242
+ end
243
+
244
+ def rubocop_config
245
+ exist?(".rubocop.yml") ? File.read(File.join(@root, ".rubocop.yml")) : ""
246
+ end
247
+
248
+ def standard_file?
249
+ exist?(".standard.yml")
250
+ end
251
+
252
+ def rubocop_file?
253
+ exist?(".rubocop.yml")
254
+ end
255
+
256
+ def dependencies_section
257
+ return @dependencies_section if defined?(@dependencies_section)
258
+
259
+ lockfile = exist?("Gemfile.lock") ? File.read(File.join(@root, "Gemfile.lock")) : nil
260
+ unless lockfile
261
+ return @dependencies_section = ""
262
+ end
263
+
264
+ lines = lockfile.lines
265
+ start = lines.index { |line| line.strip == "DEPENDENCIES" }
266
+ unless start
267
+ return @dependencies_section = ""
268
+ end
269
+
270
+ section = []
271
+ lines[(start + 1)..].each do |line|
272
+ break if line.strip.empty?
273
+
274
+ section << line
275
+ end
276
+ @dependencies_section = section.join
277
+ end
278
+
279
+ def mutation_command(sources, tests, base, rails, suite)
280
+ %w[bundle exec mutineer run] + sources + tests.flat_map { |test| ["--test", test] } +
281
+ %W[--since #{base} --threshold #{MUTATION_THRESHOLD}] + (suite == RSPEC ? %w[--framework rspec] : []) +
282
+ (rails ? %w[--rails] : %w[--strategy redefine]) +
283
+ (exist?(".mutineer.yml") ? [] : ["--operators", ALL_OPERATORS.join(",")])
284
+ end
285
+
286
+ def execute(plan, requested_base)
287
+ if plan.sources.empty? && plan.tests.empty? && plan.lint_files.empty?
288
+ @out.puts "No changed Ruby files since #{requested_base}."
289
+ return 0
290
+ end
291
+ unless plan.untested.empty?
292
+ @err.puts "No test file found for: #{plan.untested.join(", ")}. Write one, or pass --test FILE for the tests that cover it."
293
+ return 1
294
+ end
295
+ unless exist?(".mutineer.yml") || plan.sources.empty?
296
+ @err.puts "Missing .mutineer.yml: passing all 11 operators on the command line instead."
297
+ end
298
+
299
+ @out.puts "Base: #{requested_base}"
300
+ @out.puts "Sources: #{plan.sources.empty? ? "none" : plan.sources.join(", ")}"
301
+ @out.puts "Tests: #{plan.tests.join(", ")}"
302
+ @out.puts "Home: #{sandbox["HOME"]}"
303
+ FileUtils.rm_rf(File.join(@root, "coverage"))
304
+ failed = []
305
+ if plan.lint_decision.step
306
+ @out.puts "\n== #{LINT_STEP}"
307
+ failed << plan.lint_decision.step unless @run.call(plan.lint_decision.step.env, plan.lint_decision.step.command)
308
+ elsif plan.lint_decision.skip_reason
309
+ @out.puts "\nLint skipped: #{plan.lint_decision.skip_reason}"
310
+ end
311
+ plan.steps.each do |step|
312
+ @out.puts "\n== #{step.title}"
313
+ next if @run.call(step.env, step.command)
314
+
315
+ failed << step
316
+ next unless step.title == TESTS_STEP
317
+
318
+ @out.puts "\nThe tests are red, so the other checks did not run: a failing test kills every mutant."
319
+ break
320
+ end
321
+ @out.puts failed.empty? ? "\nChange verified" : "\nFailed: #{failed.map(&:title).join(", ")}"
322
+ failed.empty? ? 0 : 1
323
+ end
324
+
325
+ def rails?
326
+ exist?("config/application.rb")
327
+ end
328
+
329
+ def suite
330
+ listed = dependencies_section.match?(RSPEC_DEPENDENCY)
331
+ spec = File.directory?(File.join(@root, "spec")) && (exist?(".rspec") || exist?("spec/spec_helper.rb") || exist?("spec/rails_helper.rb"))
332
+ spec || listed ? RSPEC : MINITEST
333
+ end
334
+
335
+ def exist?(path)
336
+ File.exist?(File.join(@root, path))
337
+ end
338
+
339
+ def usage(code)
340
+ (code.zero? ? @out : @err).puts USAGE
341
+ code
342
+ end
343
+ end
344
+
345
+ exit VerifyChange.new(ARGV).call if $PROGRAM_NAME == __FILE__
package/config_asks.yml CHANGED
@@ -32,7 +32,7 @@ config_asks:
32
32
  agents: ["claude"]
33
33
  question: "Which advisor should be the default?"
34
34
  options:
35
- - label: "Faux Fable (recommended): Opus 4.8 carrying the frontier reasoning instructions. Much cheaper, available on any plan, reasons in the same disciplined way."
36
- value: "plastic-faux-advisor"
37
- - label: "Fable 5: the frontier model itself. The strongest reasoning available, billed through usage credits, so summon it for a few rounds and close it."
38
- value: "plastic-advisor"
35
+ - label: "Primary Advisor (recommended): Fable at medium effort for normal consultation."
36
+ value: "plastic-primary-advisor"
37
+ - label: "Secondary Advisor: Fable at high effort for explicit escalation."
38
+ value: "plastic-secondary-advisor"
package/deprecations.yml CHANGED
@@ -1,6 +1,6 @@
1
1
  # Plastic deprecation notices
2
2
  # Each entry is shown to the user during SessionStart.
3
- # See the plastic-releasing skill for the full convention.
3
+ # See CONTRIBUTING.md's "Cut a release" section for the full convention.
4
4
  #
5
5
  # Schema:
6
6
  # id: unique-slug (used for dismissal tracking)
@@ -2,16 +2,13 @@
2
2
 
3
3
  *Companion to the Operating Manual. How to use Fable as your advisor and planner.*
4
4
 
5
- *Adapted for Plastic (intent 185): this is the shipped reference copy the
6
- `plastic-agent-advisor` skill teaches from. Two named agents carry it,
7
- `plastic-advisor` (the real advisor) and `plastic-faux-advisor` (the cheaper
8
- imitation, the same discipline at a fraction of the cost); EFFORT and the
9
- answer shape below shape the brief and the answer on whichever one you
10
- dispatch, never which file to pick. The
11
- `plastic-agent-advisor` skill reads harness-scoped config
12
- (`advisor.claude.default`, falling back to `secondary`, then to
13
- `plastic-faux-advisor`) to route your consultation automatically; name a
14
- specific advisor in your request to override it. A frontier model rewards a
5
+ *Adapted for Plastic (intent 185): this is the shipped reference copy, reached with
6
+ `plastic help advisor-protocol`. Two named agents carry it,
7
+ `plastic-primary-advisor` (Primary Advisor) and `plastic-secondary-advisor`
8
+ (Secondary Advisor, the explicit escalation); the
9
+ answer shape below shapes the brief and the answer on whichever one you
10
+ dispatch, never which file to pick. There is no automatic routing between them: the user or
11
+ the main session dispatches one deliberately, by naming it. A frontier model rewards a
15
12
  single, well-formed one-shot brief more than a back-and-forth, so front-load
16
13
  everything section 3 below asks for before you send. The rest of this
17
14
  document is the owner's protocol, unchanged.*
@@ -53,17 +50,18 @@ You own the work and the outcome. Fable owns the hardest thinking, on demand.
53
50
 
54
51
  ---
55
52
 
56
- ## 2. Effort and answer shape
53
+ ## 2. Answer shape
57
54
 
58
55
  Classify every consultation before sending it. Default to the smallest shape and
59
- prove your way up. Never open at a higher effort "to be safe": effort follows the cost of being
60
- wrong, not the importance the task feels like it has.
56
+ prove your way up. Primary Advisor ships at medium reasoning effort. Secondary Advisor ships
57
+ at high reasoning effort on Claude Code and Codex. An explicit harness-scoped config override
58
+ may change one agent's effort.
61
59
 
62
60
  | Shape | Coding | Business | Research | Effort | Brief size | Expected return |
63
61
  |------|--------|----------|----------|--------|------------|-----------------|
64
- | **Verdict** | Verdict on one step; choose between two named implementations | Pick between two options you already compared (vendor, pricing point) | Judge whether one source or result is trustworthy enough to build on | `low` | Under 300 words | Verdict, one paragraph of reasoning, the single biggest risk |
65
- | **Plan** | Plan a feature inside one system; review a full plan for holes; design one interface; rank root causes | Positioning or pricing decision from a compiled evidence pack; review a proposal before sending it | Design a research plan for a bounded question; rank competing explanations of the data you gathered | `medium`, or `high` if an irreversible step is inside | Up to one page | Decision, numbered plan with per-step checks, risk map |
66
- | **Architecture** | Cross-system architecture; migration with one-way doors; deadlock after two failed attempts; security-critical design | Build-vs-buy, market entry, or any commitment measured in months; strategy where reversal is expensive | Synthesis across many sources where the conclusion drives a large bet; contested questions with conflicting evidence | `xhigh`; `max` only when being wrong means data loss, a broken contract, or weeks of rework | Full evidence brief | Decision, plan, risk register, kill criteria, list of what could not be verified |
62
+ | **Verdict** | Verdict on one step; choose between two named implementations | Pick between two options you already compared (vendor, pricing point) | Judge whether one source or result is trustworthy enough to build on | `medium` | Under 300 words | Verdict, one paragraph of reasoning, the single biggest risk |
63
+ | **Plan** | Plan a feature inside one system; review a full plan for holes; design one interface; rank root causes | Positioning or pricing decision from a compiled evidence pack; review a proposal before sending it | Design a research plan for a bounded question; rank competing explanations of the data you gathered | `medium` | Up to one page | Decision, numbered plan with per-step checks, risk map |
64
+ | **Architecture** | Cross-system architecture; migration with one-way doors; deadlock after two failed attempts; security-critical design | Build-vs-buy, market entry, or any commitment measured in months; strategy where reversal is expensive | Synthesis across many sources where the conclusion drives a large bet; contested questions with conflicting evidence | `medium` | Full evidence brief | Decision, plan, risk register, kill criteria, list of what could not be verified |
67
65
 
68
66
  **Escalate one shape when any of these holds:**
69
67
 
@@ -124,11 +122,8 @@ What actually raises the quality of my answer, in order of impact:
124
122
  under the Operating Manual." Then I write steps you can run at your best:
125
123
  each step with its own check, its own trap named, and its own
126
124
  stop-and-return trigger. A plan without that is half a plan.
127
- 6. **Know what effort buys.** At `low` I stress-test your candidate and give a
128
- verdict. At `medium`/`high` I generate rival solutions and compare them. At
129
- `xhigh`/`max` I build the strongest case for every rival and then try to
130
- break my own winner before you ever see it. Buy the depth the failure cost
131
- justifies, nothing more.
125
+ 6. **Use the medium default well.** Ask for the smallest answer shape that can decide the
126
+ question, and spend the available reasoning on the load-bearing risk.
132
127
  7. **Come back on the same thread.** My context is cached inside a session.
133
128
  The second question in a thread costs a fraction of the first. A new session
134
129
  pays for your whole brief again.
@@ -142,7 +137,7 @@ briefing described above; use the block as your completeness check before
142
137
  sending. Fable must never need to explore.
143
138
 
144
139
  ```
145
- SHAPE: verdict | plan | architecture EFFORT: low | medium | high | xhigh | max
140
+ SHAPE: verdict | plan | architecture
146
141
  DOMAIN: coding | business | research
147
142
  GOAL: <target state in one sentence, and the decision this answer feeds>
148
143
  QUESTIONS:
@@ -229,8 +224,8 @@ reformat, then work with what you have.
229
224
  hear yes.
230
225
  - **Drip-feeding.** Splitting one decision across many small calls, paying
231
226
  session overhead each time.
232
- - **Prestige escalation.** Requesting `max` because the task feels important.
233
- Effort follows failure cost, nothing else.
227
+ - **Prestige escalation.** Changing the medium default because the task feels important,
228
+ without evidence that a harness-scoped override will improve the decision.
234
229
  - **Unbounded questions.** "Any thoughts on this approach?" invites an essay.
235
230
  Ask for a decision with named options.
236
231
  - **Silent adoption.** Pasting Fable's plan straight into execution without