principles 0.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 (278) hide show
  1. checksums.yaml +7 -0
  2. data/exe/principles +10 -0
  3. data/lib/principles/after_commit_hook_options.rb +44 -0
  4. data/lib/principles/cli.rb +149 -0
  5. data/lib/principles/command_line.rb +188 -0
  6. data/lib/principles/command_routes.rb +60 -0
  7. data/lib/principles/controllers/acknowledgement.rb +106 -0
  8. data/lib/principles/controllers/answer.rb +172 -0
  9. data/lib/principles/controllers/audit.rb +203 -0
  10. data/lib/principles/controllers/audit_completion.rb +113 -0
  11. data/lib/principles/controllers/command.rb +201 -0
  12. data/lib/principles/controllers/correction.rb +99 -0
  13. data/lib/principles/controllers/help.rb +107 -0
  14. data/lib/principles/controllers/inventory.rb +181 -0
  15. data/lib/principles/controllers/mapping.rb +56 -0
  16. data/lib/principles/controllers/member_entries.rb +149 -0
  17. data/lib/principles/controllers/members_a_commit_changed.rb +43 -0
  18. data/lib/principles/controllers/members_to_assess.rb +160 -0
  19. data/lib/principles/controllers/meta_principle_setup.rb +92 -0
  20. data/lib/principles/controllers/repoint.rb +68 -0
  21. data/lib/principles/controllers/running_tool.rb +41 -0
  22. data/lib/principles/data/audit_files.rb +96 -0
  23. data/lib/principles/data/bounded_file.rb +45 -0
  24. data/lib/principles/data/candidate_files_found.rb +105 -0
  25. data/lib/principles/data/command-steps.json +305 -0
  26. data/lib/principles/data/command_steps_file.rb +50 -0
  27. data/lib/principles/data/expected_type.rb +71 -0
  28. data/lib/principles/data/ignored_by_git.rb +54 -0
  29. data/lib/principles/data/json_text.rb +57 -0
  30. data/lib/principles/data/known_fields.rb +42 -0
  31. data/lib/principles/data/linked_path.rb +102 -0
  32. data/lib/principles/data/map_files.rb +104 -0
  33. data/lib/principles/data/principle_id.rb +32 -0
  34. data/lib/principles/data/principles_subfolder.rb +93 -0
  35. data/lib/principles/data/project_files.rb +109 -0
  36. data/lib/principles/data/repository.rb +165 -0
  37. data/lib/principles/data/running_version.rb +44 -0
  38. data/lib/principles/data/schema_check.rb +139 -0
  39. data/lib/principles/data/schemas/acknowledgement-answer.schema.json +25 -0
  40. data/lib/principles/data/schemas/answer-guide.schema.json +99 -0
  41. data/lib/principles/data/schemas/assessment-answer.schema.json +332 -0
  42. data/lib/principles/data/schemas/audit-record.schema.json +277 -0
  43. data/lib/principles/data/schemas/command-steps.schema.json +83 -0
  44. data/lib/principles/data/schemas/correction-answer-guide.schema.json +68 -0
  45. data/lib/principles/data/schemas/correction-answer.schema.json +306 -0
  46. data/lib/principles/data/schemas/member-map.schema.json +39 -0
  47. data/lib/principles/data/schemas/repointing-answer.schema.json +44 -0
  48. data/lib/principles/error.rb +102 -0
  49. data/lib/principles/invocation_options.rb +77 -0
  50. data/lib/principles/models/accepted_loading.rb +36 -0
  51. data/lib/principles/models/acknowledgement.rb +203 -0
  52. data/lib/principles/models/activation.rb +64 -0
  53. data/lib/principles/models/agent_loads.rb +64 -0
  54. data/lib/principles/models/answer_fields.rb +67 -0
  55. data/lib/principles/models/answer_place.rb +53 -0
  56. data/lib/principles/models/answer_text.rb +37 -0
  57. data/lib/principles/models/answered_time.rb +65 -0
  58. data/lib/principles/models/assessment_answer.rb +131 -0
  59. data/lib/principles/models/assessment_basis.rb +47 -0
  60. data/lib/principles/models/assessment_report.rb +62 -0
  61. data/lib/principles/models/audit.rb +200 -0
  62. data/lib/principles/models/audit_record.rb +249 -0
  63. data/lib/principles/models/audit_records.rb +103 -0
  64. data/lib/principles/models/audit_start.rb +96 -0
  65. data/lib/principles/models/audited_member_lines.rb +45 -0
  66. data/lib/principles/models/candidate_files.rb +66 -0
  67. data/lib/principles/models/candidate_inventory.rb +67 -0
  68. data/lib/principles/models/candidate_listing.rb +32 -0
  69. data/lib/principles/models/carried_assessments.rb +76 -0
  70. data/lib/principles/models/check_since_last_audit.rb +61 -0
  71. data/lib/principles/models/cited_files.rb +39 -0
  72. data/lib/principles/models/command_steps.rb +270 -0
  73. data/lib/principles/models/commits_brought_in.rb +27 -0
  74. data/lib/principles/models/commits_since_base.rb +32 -0
  75. data/lib/principles/models/correction_answer.rb +66 -0
  76. data/lib/principles/models/correction_basis.rb +70 -0
  77. data/lib/principles/models/dated_line.rb +29 -0
  78. data/lib/principles/models/decided_prior_findings.rb +106 -0
  79. data/lib/principles/models/declared_map_changes.rb +156 -0
  80. data/lib/principles/models/disposition.rb +233 -0
  81. data/lib/principles/models/entry_file.rb +65 -0
  82. data/lib/principles/models/entry_files.rb +46 -0
  83. data/lib/principles/models/evidence_item.rb +48 -0
  84. data/lib/principles/models/evidence_lines_since.rb +57 -0
  85. data/lib/principles/models/evidence_replacements.rb +99 -0
  86. data/lib/principles/models/finding_write_ups.rb +108 -0
  87. data/lib/principles/models/findings_left_open.rb +34 -0
  88. data/lib/principles/models/first_raised_claim.rb +128 -0
  89. data/lib/principles/models/frontmatter_fields.rb +128 -0
  90. data/lib/principles/models/hook_commands.rb +100 -0
  91. data/lib/principles/models/ignored_candidates.rb +46 -0
  92. data/lib/principles/models/json_digest.rb +35 -0
  93. data/lib/principles/models/json_path_query.rb +131 -0
  94. data/lib/principles/models/json_path_selectors.rb +64 -0
  95. data/lib/principles/models/json_path_string.rb +76 -0
  96. data/lib/principles/models/kept_prior_finding.rb +70 -0
  97. data/lib/principles/models/last_completed_revision.rb +70 -0
  98. data/lib/principles/models/line_span.rb +49 -0
  99. data/lib/principles/models/loaded_files.rb +60 -0
  100. data/lib/principles/models/loading_decision.rb +45 -0
  101. data/lib/principles/models/loading_facts.rb +45 -0
  102. data/lib/principles/models/loads_to_check.rb +93 -0
  103. data/lib/principles/models/locate_refusal.rb +23 -0
  104. data/lib/principles/models/located_text.rb +43 -0
  105. data/lib/principles/models/locator.rb +52 -0
  106. data/lib/principles/models/map_members.rb +171 -0
  107. data/lib/principles/models/markdown_file.rb +92 -0
  108. data/lib/principles/models/member.rb +171 -0
  109. data/lib/principles/models/member_assessments.rb +144 -0
  110. data/lib/principles/models/member_elsewhere.rb +74 -0
  111. data/lib/principles/models/member_file.rb +156 -0
  112. data/lib/principles/models/member_grounds.rb +32 -0
  113. data/lib/principles/models/member_history.rb +68 -0
  114. data/lib/principles/models/member_lines.rb +95 -0
  115. data/lib/principles/models/member_loads.rb +197 -0
  116. data/lib/principles/models/member_map.rb +219 -0
  117. data/lib/principles/models/member_map_history.rb +143 -0
  118. data/lib/principles/models/member_maps.rb +185 -0
  119. data/lib/principles/models/member_moves.rb +95 -0
  120. data/lib/principles/models/member_problems.rb +253 -0
  121. data/lib/principles/models/member_sighting.rb +176 -0
  122. data/lib/principles/models/member_snapshots.rb +36 -0
  123. data/lib/principles/models/member_state.rb +35 -0
  124. data/lib/principles/models/members_elsewhere.rb +46 -0
  125. data/lib/principles/models/members_sharing_lines.rb +116 -0
  126. data/lib/principles/models/meta_principle.rb +51 -0
  127. data/lib/principles/models/missing_files.rb +85 -0
  128. data/lib/principles/models/misspelt_member_id.rb +45 -0
  129. data/lib/principles/models/open_finding.rb +221 -0
  130. data/lib/principles/models/open_findings_elsewhere.rb +45 -0
  131. data/lib/principles/models/other_principles.rb +47 -0
  132. data/lib/principles/models/placement.rb +42 -0
  133. data/lib/principles/models/plain_text_file.rb +32 -0
  134. data/lib/principles/models/principles_folder_move.rb +59 -0
  135. data/lib/principles/models/principles_package.rb +53 -0
  136. data/lib/principles/models/prior_finding_decision.rb +162 -0
  137. data/lib/principles/models/prior_finding_outcomes.rb +60 -0
  138. data/lib/principles/models/prior_findings.rb +109 -0
  139. data/lib/principles/models/problem_report.rb +110 -0
  140. data/lib/principles/models/project.rb +214 -0
  141. data/lib/principles/models/project_history.rb +203 -0
  142. data/lib/principles/models/record_judged_against.rb +42 -0
  143. data/lib/principles/models/recorded_entry.rb +93 -0
  144. data/lib/principles/models/recorded_finding.rb +97 -0
  145. data/lib/principles/models/recorded_findings.rb +58 -0
  146. data/lib/principles/models/recorded_members.rb +149 -0
  147. data/lib/principles/models/repoint.rb +112 -0
  148. data/lib/principles/models/reported_check.rb +130 -0
  149. data/lib/principles/models/request_id.rb +70 -0
  150. data/lib/principles/models/runs_from.rb +36 -0
  151. data/lib/principles/models/skill_loads.rb +123 -0
  152. data/lib/principles/models/still_holds_restatement.rb +87 -0
  153. data/lib/principles/models/text_size.rb +26 -0
  154. data/lib/principles/models/uncommitted_outside_the_map.rb +30 -0
  155. data/lib/principles/models/waits_on.rb +58 -0
  156. data/lib/principles/models/write_up_opening.rb +93 -0
  157. data/lib/principles/models/write_up_pointer.rb +50 -0
  158. data/lib/principles/models/write_up_repoint.rb +39 -0
  159. data/lib/principles/prose_list.rb +23 -0
  160. data/lib/principles/supported_ruby.rb +93 -0
  161. data/lib/principles/version.rb +11 -0
  162. data/lib/principles/views/acknowledged_since_last_audit.rb +34 -0
  163. data/lib/principles/views/acknowledgement_result.rb +44 -0
  164. data/lib/principles/views/activation_in_hand.rb +47 -0
  165. data/lib/principles/views/assessment_request.rb +131 -0
  166. data/lib/principles/views/audit_changes.rb +79 -0
  167. data/lib/principles/views/audit_commit.rb +55 -0
  168. data/lib/principles/views/audit_result.rb +147 -0
  169. data/lib/principles/views/candidate_files_result.rb +95 -0
  170. data/lib/principles/views/check_result_template.rb +75 -0
  171. data/lib/principles/views/diff_since_last_audit.rb +29 -0
  172. data/lib/principles/views/findings_result.rb +45 -0
  173. data/lib/principles/views/help.rb +119 -0
  174. data/lib/principles/views/invocation.rb +66 -0
  175. data/lib/principles/views/landed_since_base.rb +27 -0
  176. data/lib/principles/views/lines_shared_with_member.rb +111 -0
  177. data/lib/principles/views/loads_to_check_result.rb +45 -0
  178. data/lib/principles/views/map_result.rb +169 -0
  179. data/lib/principles/views/member_change_since_baseline.rb +58 -0
  180. data/lib/principles/views/member_repairs.rb +82 -0
  181. data/lib/principles/views/members_a_commit_changed.rb +66 -0
  182. data/lib/principles/views/members_with_no_baseline.rb +56 -0
  183. data/lib/principles/views/meta_principle_passage.rb +23 -0
  184. data/lib/principles/views/meta_principle_result.rb +170 -0
  185. data/lib/principles/views/open_findings_elsewhere_result.rb +32 -0
  186. data/lib/principles/views/open_findings_line.rb +125 -0
  187. data/lib/principles/views/other_principles_unjudged_result.rb +32 -0
  188. data/lib/principles/views/outside_the_map_result.rb +31 -0
  189. data/lib/principles/views/package_since_last_audit_result.rb +34 -0
  190. data/lib/principles/views/principle_read_result.rb +86 -0
  191. data/lib/principles/views/principle_with_changed_members.rb +26 -0
  192. data/lib/principles/views/principles_with_no_completed_audit.rb +30 -0
  193. data/lib/principles/views/printed_principle.rb +45 -0
  194. data/lib/principles/views/prior_finding_decisions_result.rb +35 -0
  195. data/lib/principles/views/prior_findings_result.rb +80 -0
  196. data/lib/principles/views/problem_lines.rb +84 -0
  197. data/lib/principles/views/problem_result.rb +107 -0
  198. data/lib/principles/views/prompts/acknowledge-a-change/say-whether-the-change-keeps-the-meaning.md +12 -0
  199. data/lib/principles/views/prompts/answer-any-judgement-request/what-a-self-audit-cannot-show.md +6 -0
  200. data/lib/principles/views/prompts/assess-every-member/answer-guide.json +63 -0
  201. data/lib/principles/views/prompts/assess-every-member/assess-each-member-and-the-set.md +12 -0
  202. data/lib/principles/views/prompts/assess-every-member/check-roles-other-principles-give-these-lines.md +7 -0
  203. data/lib/principles/views/prompts/assess-every-member/decide-each-member-loading.md +17 -0
  204. data/lib/principles/views/prompts/assess-every-member/decide-each-prior-finding.md +38 -0
  205. data/lib/principles/views/prompts/assess-every-member/give-problem-evidence-at-still-holds.md +2 -0
  206. data/lib/principles/views/prompts/assess-every-member/give-the-evidence-a-prior-finding-lacks.md +7 -0
  207. data/lib/principles/views/prompts/assess-every-member/have-a-changed-check-reviewed-before-running-it.md +4 -0
  208. data/lib/principles/views/prompts/assess-every-member/have-a-new-check-reviewed-before-running-it.md +3 -0
  209. data/lib/principles/views/prompts/assess-every-member/have-hidden-characters-in-a-check-explained.md +4 -0
  210. data/lib/principles/views/prompts/assess-every-member/judge-acknowledged-edits-together.md +6 -0
  211. data/lib/principles/views/prompts/assess-every-member/judge-the-keeping-against-the-other-principles.md +7 -0
  212. data/lib/principles/views/prompts/assess-every-member/judge-the-keeping-under-the-release-since-the-last-audit.md +10 -0
  213. data/lib/principles/views/prompts/assess-every-member/judge-the-set-with-carried-assessments.md +10 -0
  214. data/lib/principles/views/prompts/assess-every-member/keep-the-last-review-basis-unless-you-give-a-new-one.md +1 -0
  215. data/lib/principles/views/prompts/assess-every-member/name-another-principles-open-finding-rather-than-raise-it.md +9 -0
  216. data/lib/principles/views/prompts/assess-every-member/read-whether-each-prior-finding-s-evidence-changed.md +12 -0
  217. data/lib/principles/views/prompts/assess-every-member/run-a-named-check-at-every-full-audit.md +2 -0
  218. data/lib/principles/views/prompts/assess-every-member/run-the-check-then-report-it.md +2 -0
  219. data/lib/principles/views/prompts/assess-every-member/run-the-reviewed-check-and-say-what-its-review-rested-on.md +6 -0
  220. data/lib/principles/views/prompts/assess-every-member/uncommitted-files-outside-the-map.md +4 -0
  221. data/lib/principles/views/prompts/assess-every-member/why-each-member-is-judged-again.md +24 -0
  222. data/lib/principles/views/prompts/dispose-of-each-finding/answer-guide.json +34 -0
  223. data/lib/principles/views/prompts/dispose-of-each-finding/fix-or-record-each-finding.md +24 -0
  224. data/lib/principles/views/prompts/find-a-principle-to-map/ask-the-owner-before-mapping.md +1 -0
  225. data/lib/principles/views/prompts/find-a-principle-to-map/choose-one-principle.md +5 -0
  226. data/lib/principles/views/prompts/find-a-principle-to-map/owner-ask-ignored-folders.md +2 -0
  227. data/lib/principles/views/prompts/find-a-principle-to-map/owner-ask-ignored.md +3 -0
  228. data/lib/principles/views/prompts/find-a-principle-to-map/owner-ask-possibly-test-data.md +2 -0
  229. data/lib/principles/views/prompts/find-a-principle-to-map/owner-ask-untracked.md +2 -0
  230. data/lib/principles/views/prompts/find-a-principle-to-map/what-each-command-writes.md +4 -0
  231. data/lib/principles/views/prompts/find-a-principle-to-map/where-the-inventory-looks.md +10 -0
  232. data/lib/principles/views/prompts/learn-what-a-command-does/add-member.md +45 -0
  233. data/lib/principles/views/prompts/learn-what-a-command-does/change-activation.md +21 -0
  234. data/lib/principles/views/prompts/learn-what-a-command-does/change-description.md +2 -0
  235. data/lib/principles/views/prompts/learn-what-a-command-does/change-member-check.md +17 -0
  236. data/lib/principles/views/prompts/learn-what-a-command-does/change-member-role.md +9 -0
  237. data/lib/principles/views/prompts/learn-what-a-command-does/create-principle.md +6 -0
  238. data/lib/principles/views/prompts/learn-what-a-command-does/every-command-and-how-they-chain.md +28 -0
  239. data/lib/principles/views/prompts/learn-what-a-command-does/list-candidate-files.md +17 -0
  240. data/lib/principles/views/prompts/learn-what-a-command-does/list-member-problems.md +73 -0
  241. data/lib/principles/views/prompts/learn-what-a-command-does/list-open-findings.md +71 -0
  242. data/lib/principles/views/prompts/learn-what-a-command-does/list-principles.md +7 -0
  243. data/lib/principles/views/prompts/learn-what-a-command-does/move-member.md +11 -0
  244. data/lib/principles/views/prompts/learn-what-a-command-does/remove-member-check.md +10 -0
  245. data/lib/principles/views/prompts/learn-what-a-command-does/remove-member.md +6 -0
  246. data/lib/principles/views/prompts/learn-what-a-command-does/show-answer-check.md +16 -0
  247. data/lib/principles/views/prompts/learn-what-a-command-does/show-audit.md +20 -0
  248. data/lib/principles/views/prompts/learn-what-a-command-does/show-meta-principle-passage.md +52 -0
  249. data/lib/principles/views/prompts/learn-what-a-command-does/show-principle.md +86 -0
  250. data/lib/principles/views/prompts/learn-what-a-command-does/start-audit.md +68 -0
  251. data/lib/principles/views/prompts/learn-what-a-command-does/submit-answer.md +56 -0
  252. data/lib/principles/views/prompts/repoint-write-ups/point-each-moved-write-up-at-its-new-place.md +10 -0
  253. data/lib/principles/views/prompts/report-after-a-commit/it-refused-after-the-commits-touched-the-maps.md +2 -0
  254. data/lib/principles/views/prompts/report-after-a-commit/others-are-still-listed.md +1 -0
  255. data/lib/principles/views/prompts/report-after-a-commit/the-commits-changed-these-members.md +2 -0
  256. data/lib/principles/views/prompts/report-after-a-commit/this-command-shows-each-one.md +1 -0
  257. data/lib/principles/views/prompts/report-the-completed-audit/commit-only-what-the-audit-changed.md +2 -0
  258. data/lib/principles/views/prompts/report-the-completed-audit/replace-the-default-commit-message.md +4 -0
  259. data/lib/principles/views/prompts/report-the-completed-audit/what-completion-means.md +2 -0
  260. data/lib/principles/views/prompts/set-principles-up/entry-file-passage.md +3 -0
  261. data/lib/principles/views/prompts/set-principles-up/name-the-step-each-member-lacks.md +15 -0
  262. data/lib/principles/views/prompts/set-principles-up/place-the-passage-with-the-user.md +31 -0
  263. data/lib/principles/views/prompts/set-principles-up/set-up-first.md +2 -0
  264. data/lib/principles/views/prompts/set-principles-up/what-the-passage-presumes.md +4 -0
  265. data/lib/principles/views/prompts/start-a-session/act-on-the-open-findings.md +5 -0
  266. data/lib/principles/views/prompts/start-a-session/it-returns-after-each-clear-or-compaction-too.md +1 -0
  267. data/lib/principles/views/prompts/start-a-session/it-returns-at-each-session-start.md +1 -0
  268. data/lib/principles/views/prompts/start-a-session/raise-them-at-the-next-pause.md +1 -0
  269. data/lib/principles/views/prompts/start-a-session/raise-them-before-the-first-task.md +1 -0
  270. data/lib/principles/views/prompts.rb +71 -0
  271. data/lib/principles/views/repoint_result.rb +80 -0
  272. data/lib/principles/views/request_part.rb +26 -0
  273. data/lib/principles/views/request_parts.rb +26 -0
  274. data/lib/principles/views/shell_word.rb +29 -0
  275. data/lib/principles/views/visible_text.rb +26 -0
  276. data/lib/principles/views/workflow_diagram.rb +77 -0
  277. data/lib/principles.rb +5 -0
  278. metadata +326 -0
@@ -0,0 +1,111 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ require_relative "../prose_list"
5
+
6
+ module Principles
7
+ # What this tool prints, from plain values a controller hands it. views/CLAUDE.md owns the boundary.
8
+ module Views
9
+ # What add-member's result says of the members sharing a line with the member it added, show-principle
10
+ # beside each member it shows (#541), and a full audit's request beside each member, of other maps only (#719):
11
+ # those in its map (overlaps), those of them spanning exactly its lines
12
+ # (same_lines_as, #442), and those in other maps, with their roles there and same_lines when they span exactly
13
+ # its lines, spans_these_lines when their lines include all of its and within_these_lines when its lines
14
+ # include theirs (mapped_elsewhere, #383, #718). Each field is printed only when there are some; add-member
15
+ # also prints the sentences that say what it means for the agent.
16
+ class LinesSharedWithMember
17
+ # Why the agent checks the text where the lines meet, unless each member holding another's lines is a whole
18
+ # file, which holds every line's text (#756).
19
+ TEXT_CHECK = "Lines are not text: where two share a first or last line, a passage can start or end partway " \
20
+ "through it, so check the text there before deciding which edit reaches which member. " #: String
21
+
22
+ # text_check says whether a member holding another's lines is not a whole file, so the text needs checking.
23
+ #: (?overlaps: Array[String], ?same_lines_as: Array[String], ?mapped_elsewhere: Array[Hash[String, untyped]],
24
+ #| ?text_check: bool) -> void
25
+ def initialize(overlaps: [], same_lines_as: [], mapped_elsewhere: [], text_check: false)
26
+ @overlaps = overlaps
27
+ @same_lines_as = same_lines_as
28
+ @mapped_elsewhere = mapped_elsewhere
29
+ @text_check = text_check
30
+ freeze
31
+ end
32
+
33
+ # The fields to merge into added, each only when there are some.
34
+ #: () -> Hash[String, untyped]
35
+ def to_h
36
+ { "overlaps" => @overlaps, "same_lines_as" => @same_lines_as, "mapped_elsewhere" => @mapped_elsewhere }
37
+ .reject { |_field, shared| shared.empty? }
38
+ end
39
+
40
+ # The sentences the question carries for them, or none.
41
+ #: () -> String
42
+ def note
43
+ "#{overlap_note}#{elsewhere_note}#{same_lines_note}"
44
+ end
45
+
46
+ private
47
+
48
+ #: () -> String
49
+ def overlap_note
50
+ return "" if @overlaps.empty?
51
+
52
+ "It shares lines with #{@overlaps.join(", ")}, so one edit there will show as a change to each. "
53
+ end
54
+
55
+ #: () -> String
56
+ def elsewhere_note
57
+ return "" if @mapped_elsewhere.empty?
58
+
59
+ "Members of other principles share lines with this one (mapped_elsewhere), so an edit to the lines they " \
60
+ "share changes each. #{spans_note}Check that this member's role fits beside the roles they give it there. "
61
+ end
62
+
63
+ # Which of them span all of this member's lines and which lie within them, so the agent can work out which
64
+ # member an edit reaches (#718). Lines are not text, so the agent checks where they share a first or last line.
65
+ #: () -> String
66
+ def spans_note
67
+ spans = @mapped_elsewhere.filter_map do |each|
68
+ if each["spans_these_lines"] then spanning(each)
69
+ elsif each["within_these_lines"] then within(each)
70
+ end
71
+ end
72
+ "#{spans.join}#{TEXT_CHECK if @text_check && spans.any?}"
73
+ end
74
+
75
+ # One that spans all of this member's lines: an edit to this member reaches it, and one outside them only it.
76
+ #: (Hash[String, untyped]) -> String
77
+ def spanning(each)
78
+ id = "`#{each.fetch("member_id")}`"
79
+ "#{id} in #{each.fetch("principle_id")} spans all of this member's lines, so every edit to this member's " \
80
+ "text is an edit to it too, and an edit outside the shared lines reaches only #{id}. "
81
+ end
82
+
83
+ # One within this member's lines: an edit to it reaches this member, and one outside them only this member.
84
+ #: (Hash[String, untyped]) -> String
85
+ def within(each)
86
+ "`#{each.fetch("member_id")}` in #{each.fetch("principle_id")} lies within this member's lines, so every " \
87
+ "edit to its text is an edit to this member's too, and an edit outside the shared lines reaches only " \
88
+ "this member. "
89
+ end
90
+
91
+ # Equal lines are not equal text, as a passage can end within its last line, so the agent decides.
92
+ #: () -> String
93
+ def same_lines_note
94
+ return "" if @same_lines_as.empty? && @mapped_elsewhere.none? { |each| each["same_lines"] }
95
+
96
+ named = same_lines_named
97
+ "#{ProseList.new(named)} #{named.length > 1 ? "span" : "spans"} exactly these lines " \
98
+ "(same_lines), under another id. If they are meant to be one member, decide which to keep; if both are " \
99
+ "meant, say why in this member's role. "
100
+ end
101
+
102
+ # Each member spanning exactly these lines, by id, with its principle when it is in another (#445).
103
+ #: () -> Array[String]
104
+ def same_lines_named
105
+ elsewhere = @mapped_elsewhere.select { |each| each["same_lines"] }
106
+ @same_lines_as.map { |id| "`#{id}`" } +
107
+ elsewhere.map { |each| "`#{each.fetch("member_id")}` in #{each.fetch("principle_id")}" }
108
+ end
109
+ end
110
+ end
111
+ end
@@ -0,0 +1,45 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ require_relative "invocation"
5
+ require_relative "prompts"
6
+ require_relative "request_part"
7
+
8
+ module Principles
9
+ # What this tool prints, from plain values a controller hands it. views/CLAUDE.md owns the boundary.
10
+ module Views
11
+ # How a full audit's request shows the members to check against the principle's activation (#355 slice 3).
12
+ class LoadsToCheckResult
13
+ # listed is the members to check, nil when there are none, and id the principle they belong to.
14
+ #: (Invocation, Hash[String, untyped]?, String) -> void
15
+ def initialize(invocation, listed, id)
16
+ @invocation = invocation
17
+ @listed = listed
18
+ @id = id
19
+ freeze
20
+ end
21
+
22
+ # The request's part that lists the members to check against the activation, how to decide them, and a
23
+ # decision to fill in for each; nil when there are none to list.
24
+ #: () -> RequestPart?
25
+ def part
26
+ listed = @listed or return
27
+
28
+ decisions = listed.fetch("rows").map { |row| { "member_id" => row.fetch("member_id"), "decision" => nil } }
29
+ RequestPart.new(shown: { "loads_to_check_against_activation" => listed,
30
+ "loads_to_check_instruction" => instruction },
31
+ slots: { "loading_decisions" => decisions })
32
+ end
33
+
34
+ private
35
+
36
+ # How to decide each listed member, naming the activation change for a principle whose work is in hand in
37
+ # every session (#509).
38
+ #: () -> String
39
+ def instruction
40
+ always = @invocation.in_project("change-activation", @id, options: ["--always"])
41
+ Prompts.text("assess-every-member/decide-each-member-loading", always: always)
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,169 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ require_relative "invocation"
5
+ require_relative "printed_principle"
6
+ require_relative "lines_shared_with_member"
7
+ require_relative "activation_in_hand"
8
+ require_relative "prompts"
9
+
10
+ module Principles
11
+ # What this tool prints, from plain values a controller hands it. views/CLAUDE.md owns the boundary.
12
+ module Views
13
+ # The JSON results of the commands that write a principle's map, with commands for the invocation running now.
14
+ # PrincipleReadResult prints the reads.
15
+ class MapResult
16
+ # The placeholders each printed command leaves for the agent to fill.
17
+ ADD_MEMBER_USAGE = "PATH [--section \"Exact heading text\"] [--starting-at \"text that occurs once\" " \
18
+ "[--ending-at \"text that closes the passage\"]] [--query '$.description'] " \
19
+ "--role \"The part it plays\""
20
+
21
+ CREATED = "Which files or Markdown sections reinforce this principle? Add each one with the part it plays, " \
22
+ "for example: states the principle, lists the checks, records settled terms, reminds on a timer, " \
23
+ "fails the build. When a member is one item inside a larger file or section, narrow it with " \
24
+ "--starting-at, and --ending-at for more than one line. Add one member per command. Leave out the " \
25
+ "material the members govern."
26
+
27
+ # in_git says whether the project is in git, where a committed map is each member's baseline until its first
28
+ # full audit (#623).
29
+ #: (Invocation, in_git: bool) -> void
30
+ def initialize(invocation, in_git:)
31
+ @invocation = invocation
32
+ @in_git = in_git
33
+ freeze
34
+ end
35
+
36
+ #: (PrintedPrinciple) -> Hash[String, untyped]
37
+ def created(principle)
38
+ question(principle, CREATED, add_member_command(principle.id))
39
+ end
40
+
41
+ #: (PrintedPrinciple) -> Hash[String, untyped]
42
+ def description_changed(principle)
43
+ question(principle, "Changed the description of #{principle.id}.", start_audit(principle.id))
44
+ end
45
+
46
+ # What this tool recorded of when the work is in hand, and what a full audit lists against it, as
47
+ # Models::Activation#full_audit_lists names it (#355, #523).
48
+ #: (PrintedPrinciple, lists: String) -> Hash[String, untyped]
49
+ def activation_changed(principle, lists:)
50
+ question(principle, ActivationInHand.new(principle.activation || {}).recorded(principle.id, lists),
51
+ start_audit(principle.id), shown: { "activation" => principle.activation })
52
+ end
53
+
54
+ # change-activation when the map already held that activation, so it wrote nothing (#474).
55
+ #: (PrintedPrinciple) -> Hash[String, untyped]
56
+ def activation_unchanged(principle)
57
+ question(principle, ActivationInHand.new(principle.activation || {}).unchanged(principle.id),
58
+ @invocation.in_project("show-principle", principle.id),
59
+ shown: { "activation" => principle.activation })
60
+ end
61
+
62
+ # shared says which members share a line with the new one; added lists them only when there are some, as it
63
+ # carries a check only when one is named.
64
+ #: (PrintedPrinciple, added: Hash[String, untyped], ?shared: LinesSharedWithMember) -> Hash[String, untyped]
65
+ def member_added(principle, added:, shared: LinesSharedWithMember.new)
66
+ added = added.merge(shared.to_h)
67
+ shown = @invocation.in_project("show-principle", principle.id)
68
+ question(principle, "#{span(added)}#{shared.note}Add the next member the same way. Commit " \
69
+ "#{principle.file} as it changes, so the project's history shows each change; in git, " \
70
+ "start-audit refuses a map that is not committed.\n" \
71
+ "To see the map so far: #{shown}\n#{start_when_mapped(principle.id)}",
72
+ add_member_command(principle.id), shown: { "member_count" => principle.members.length })
73
+ .merge("added" => added)
74
+ end
75
+
76
+ # add-member's report of the map it started, with the description and activation it wrote, which the agent
77
+ # never typed (#929).
78
+ #: (PrintedPrinciple) -> Hash[String, untyped]
79
+ def map_created(principle)
80
+ { "map_created" => { "description" => principle.description, "activation" => principle.activation } }
81
+ end
82
+
83
+ # move-member: the member's old id, and its new id with the lines it now spans.
84
+ #: (PrintedPrinciple, from: String, moved: Hash[String, untyped]) -> Hash[String, untyped]
85
+ def member_moved(principle, from:, moved:)
86
+ id = moved.fetch("id")
87
+ question(principle, "#{id} now spans lines #{moved.fetch("first_line")} to #{moved.fetch("last_line")}" \
88
+ "#{" (moved from #{from})" unless from == id}; if that is not the passage you meant, " \
89
+ "move it again. Check the other members, then start the audit.", start_audit(principle.id))
90
+ .merge("moved" => moved)
91
+ end
92
+
93
+ # change-member-check: the stored command is the member's check in the map printed beside this, so the
94
+ # agent can see it byte for byte; the question names that field rather than quoting project text (#227).
95
+ # hidden names each character in the check that prints escaped or not at all (#260).
96
+ #: (PrintedPrinciple, member: String, hidden: Array[String]) -> Hash[String, untyped]
97
+ def check_changed(principle, member:, hidden:)
98
+ done = "#{member} is now enforced by the check named in its map entry. The next audit marks it new or " \
99
+ "changed and asks for a review before it runs; nothing runs it automatically: " \
100
+ "you run it when an audit asks for its result."
101
+ return member_replaced(principle, member: member, done: done) if hidden.empty?
102
+
103
+ member_replaced(principle, member: member,
104
+ done: "#{done} The check holds characters that print escaped or not at all, " \
105
+ "which check_hidden_characters names; make sure each is meant.")
106
+ .merge("check_hidden_characters" => hidden)
107
+ end
108
+
109
+ # change-member-role, change-member-check and remove-member-check: done says what changed. This tool cannot
110
+ # tell in git whether a full audit's correction is open, so the next step names both cases (D40, #461), and
111
+ # the other case by what the agent holds: a correction request (second alpha user on 8e5b3f9).
112
+ #: (PrintedPrinciple, member: String, done: String) -> Hash[String, untyped]
113
+ def member_replaced(principle, member:, done:)
114
+ question(principle, "#{done} Check the other members, then start the audit.\nIf you hold a correction " \
115
+ "request from submit-answer (phase correction), list #{member} in the " \
116
+ "changed_map_entries of the fixed disposition this change fixes, and submit it instead: " \
117
+ "#{@invocation.submit_answer("REQUEST")}",
118
+ start_audit(principle.id))
119
+ end
120
+
121
+ #: (PrintedPrinciple, removed: String) -> Hash[String, untyped]
122
+ def member_removed(principle, removed:)
123
+ question(principle, "Removed #{removed}. Add or remove members until the map is right.\n" \
124
+ "#{start_when_mapped(principle.id)}",
125
+ add_member_command(principle.id))
126
+ end
127
+
128
+ private
129
+
130
+ # The lines a member spans, said so that a passage cut short shows; it ends with a space.
131
+ #: (Hash[String, untyped]) -> String
132
+ def span(member)
133
+ "#{member.fetch("id")} spans lines #{member.fetch("first_line")} to #{member.fetch("last_line")}; " \
134
+ "if that is not the passage you meant, move it with move-member. "
135
+ end
136
+
137
+ # shown is what the result prints of the map: the whole map unless the caller names less, as add-member
138
+ # does (#400).
139
+ #: (PrintedPrinciple, String, String, ?shown: Hash[String, untyped]) -> Hash[String, untyped]
140
+ def question(principle, text, answer_with, shown: { "map" => principle.map })
141
+ { "status" => "question", "principle_id" => principle.id, "map_file" => principle.file, **shown,
142
+ "question" => text, "answer_with" => answer_with }
143
+ end
144
+
145
+ #: (String) -> String
146
+ def start_audit(id)
147
+ @invocation.in_project("start-audit", id)
148
+ end
149
+
150
+ # The step after adding members, as the question's last line, since answer_with is the next add-member.
151
+ #: (String) -> String
152
+ def start_when_mapped(id)
153
+ if @in_git
154
+ return "When the map lists every member, commit it: list-member-problems then reports any change to a " \
155
+ "member since that commit. Only a full audit judges whether they reinforce the principle.\n" \
156
+ "To start it when you choose: #{start_audit(id)}"
157
+ end
158
+
159
+ "When the map lists every member, start the audit. It judges whether they reinforce the principle, and " \
160
+ "gives each member the baseline list-member-problems compares it with.\nTo start it: #{start_audit(id)}"
161
+ end
162
+
163
+ #: (String) -> String
164
+ def add_member_command(id)
165
+ @invocation.in_project("add-member", id, usage: ADD_MEMBER_USAGE)
166
+ end
167
+ end
168
+ end
169
+ end
@@ -0,0 +1,58 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ require_relative "shell_word"
5
+
6
+ module Principles
7
+ # What this tool prints, from plain values a controller hands it. views/CLAUDE.md owns the boundary.
8
+ module Views
9
+ # One member's change since its baseline, as a one-member audit's request shows it: the member's id, role and
10
+ # evidence, the commit its baseline text is at, the commit the audit is based on, the member's own lines at
11
+ # each (#379), and the command that shows its file's change between them, following a move (D33, #551).
12
+ class MemberChangeSinceBaseline
13
+ # member holds the member's id, role and evidence; commits holds "base", HEAD, and "baseline", the commit
14
+ # the member's baseline text is at, and "audit_base" when the baseline is later than the audit's base;
15
+ # member_lines holds its own lines at each, or their counts when too long; paths holds the paths the change
16
+ # spans, its path at the baseline first.
17
+ #: (member: Hash[String, untyped], commits: Hash[String, String], member_lines: Hash[String, untyped],
18
+ #| paths: Array[String]) -> void
19
+ def initialize(member:, commits:, member_lines:, paths:)
20
+ @member = member
21
+ @commits = commits
22
+ @member_lines = member_lines
23
+ @paths = paths
24
+ freeze
25
+ end
26
+
27
+ #: () -> String
28
+ def member_id
29
+ @member.fetch("id")
30
+ end
31
+
32
+ #: () -> String
33
+ def base_commit
34
+ @commits.fetch("base")
35
+ end
36
+
37
+ # The fields as the request prints them. Lines left out for their length point at read_the_change instead,
38
+ # so the request is never silently partial (D38).
39
+ #: () -> Hash[String, untyped]
40
+ def to_h
41
+ read = read_the_change
42
+ lines = @member_lines.key?("omitted") ? @member_lines.merge("read_instead" => read) : @member_lines
43
+ { "member" => @member, "baseline_commit" => @commits.fetch("baseline"), "base_commit" => base_commit,
44
+ **@commits.slice("audit_base").transform_keys { "audit_base_commit" },
45
+ "member_lines" => lines, "read_the_change" => read }
46
+ end
47
+
48
+ private
49
+
50
+ # Each word quoted for the shell, and every path read by git as written, not as a pattern (#558).
51
+ #: () -> String
52
+ def read_the_change
53
+ words = [@commits.fetch("baseline"), base_commit, "--", *@paths].map { |word| ShellWord.new(word).to_s }
54
+ "git --literal-pathspecs diff -M #{words.join(" ")}"
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,82 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ require_relative "invocation"
5
+
6
+ module Principles
7
+ # What this tool prints, from plain values a controller hands it. views/CLAUDE.md owns the boundary.
8
+ module Views
9
+ # The commands that repair a member this tool cannot find: move it, or remove it. A member whose file is
10
+ # gone is moved to the path the agent finds, keeping its own section, passage or query, so a rename keeps
11
+ # its audit (#268). A member whose place in its file is gone is moved within it, or to another file its text
12
+ # moved into (#378); a passage keeps its anchors, so a move cannot silently widen it to its whole new
13
+ # section (#251). Which fits is the agent's to judge.
14
+ class MemberRepairs
15
+ # The flag that names each locator field in a printed command.
16
+ FLAGS = { "section" => "--section", "starting_at" => "--starting-at", "ending_at" => "--ending-at",
17
+ "query" => "--query" }.freeze #: Hash[String, String]
18
+
19
+ #: (Invocation) -> void
20
+ def initialize(invocation)
21
+ @invocation = invocation
22
+ freeze
23
+ end
24
+
25
+ # The repairs for member, a printed member with its "id", its locator fields and the code of the refusal
26
+ # that says why it cannot be found, as "code" or show-principle's "error", of principle id. Each is
27
+ # copyable as it is: move-member, with NEW_PATH, HEADING or QUERY to fill in, then, for a section or
28
+ # passage lost from a file that is still there, the same move into another file, then remove-member.
29
+ #: (String, Hash[String, untyped]) -> Array[String]
30
+ def commands(id, member)
31
+ member_id = member.fetch("id")
32
+ [move(id, member_id, member), *move_elsewhere(id, member_id, member),
33
+ @invocation.in_project("remove-member", id, member_id)]
34
+ end
35
+
36
+ # member, as show-principle prints it, with its repairs as repair_with when broken, which the controller says
37
+ # of a member an audit would refuse (#272).
38
+ #: (String, Hash[String, untyped], broken: bool) -> Hash[String, untyped]
39
+ def beside(id, member, broken:)
40
+ return member unless broken
41
+
42
+ member.merge("repair_with" => commands(id, member))
43
+ end
44
+
45
+ private
46
+
47
+ #: (String, String, Hash[String, untyped]) -> String
48
+ def move(id, member_id, member)
49
+ if file_unreadable?(member)
50
+ kept = flags(member, FLAGS.keys)
51
+ return @invocation.in_project("move-member", id, member_id, options: kept, usage: "--path NEW_PATH")
52
+ end
53
+ return @invocation.in_project("move-member", id, member_id, usage: "--query QUERY") if member["query"]
54
+
55
+ @invocation.in_project("move-member", id, member_id, options: flags(member, %w[starting_at ending_at]),
56
+ usage: "--section HEADING")
57
+ end
58
+
59
+ # A section or passage lost from a file that is still there may have moved into another file (#378).
60
+ #: (String, String, Hash[String, untyped]) -> Array[String]
61
+ def move_elsewhere(id, member_id, member)
62
+ return [] if member["query"] || file_unreadable?(member)
63
+
64
+ anchors = flags(member, %w[starting_at ending_at])
65
+ [@invocation.in_project("move-member", id, member_id, options: anchors,
66
+ usage: "--path NEW_PATH --section HEADING")]
67
+ end
68
+
69
+ # Whether member's file cannot be read, by the refusal it carries.
70
+ #: (Hash[String, untyped]) -> bool
71
+ def file_unreadable?(member)
72
+ (member["code"] || member.dig("error", "code")) == "unreadable_file"
73
+ end
74
+
75
+ # The flags, with their values, of the locator fields named in keys that member has.
76
+ #: (Hash[String, untyped], Array[String]) -> Array[String]
77
+ def flags(member, keys)
78
+ keys.select { |key| member[key] }.flat_map { |key| [FLAGS.fetch(key), member.fetch(key)] }
79
+ end
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,66 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ require "json"
5
+ require_relative "invocation"
6
+ require_relative "problem_lines"
7
+ require_relative "prompts"
8
+ require_relative "visible_text"
9
+
10
+ module Principles
11
+ # What this tool prints, from plain values a controller hands it. views/CLAUDE.md owns the boundary.
12
+ module Views
13
+ # list-member-problems --commits-after COMMIT --hook-event EVENT: the whole JSON the plugin's after-commit hook
14
+ # prints for Claude Code, which puts its additionalContext beside the Bash call's result (D73), or nothing at all.
15
+ # Claude Code asks for that text to state facts, not to give orders, so it says what the commits changed and
16
+ # which command shows more.
17
+ class MembersACommitChanged
18
+ NOTHING = Lines.new(stdout: "", stderr: "", status: 0).freeze #: Lines
19
+
20
+ #: (Invocation) -> void
21
+ def initialize(invocation)
22
+ @invocation = invocation
23
+ freeze
24
+ end
25
+
26
+ # changed is each member the commits changed, as list-member-problems lists it; others is how many other
27
+ # changed members it still lists.
28
+ #: (String, Array[Hash[String, untyped]], others: Integer) -> Lines
29
+ def changed(event, changed, others:)
30
+ rows = changed.map { |member| ProblemLines.new(@invocation).row(member) }
31
+ reported(event, [Prompts.text("report-after-a-commit/the-commits-changed-these-members"), *rows,
32
+ *still_listed(others), Prompts.text("report-after-a-commit/this-command-shows-each-one"),
33
+ command])
34
+ end
35
+
36
+ # A refusal with code, which the commits could have caused.
37
+ #: (String, String) -> Lines
38
+ def refused(event, code)
39
+ reported(event, [Prompts.text("report-after-a-commit/it-refused-after-the-commits-touched-the-maps",
40
+ code: VisibleText.new(code).to_s), command])
41
+ end
42
+
43
+ private
44
+
45
+ # The line saying how many other changed members list-member-problems still lists, or none when it lists none.
46
+ #: (Integer) -> Array[String]
47
+ def still_listed(others)
48
+ return [] if others.zero?
49
+
50
+ [Prompts.text("report-after-a-commit/others-are-still-listed",
51
+ count: others.to_s, members: others == 1 ? "member is" : "members are")]
52
+ end
53
+
54
+ #: () -> String
55
+ def command = @invocation.in_project("list-member-problems")
56
+
57
+ # The hook's JSON. Each field from a project file or commit is written through VisibleText already, so an
58
+ # invisible character reaches the model as \uXXXX, not hidden in the text it reads (#227).
59
+ #: (String, Array[String]) -> Lines
60
+ def reported(event, lines)
61
+ output = { "hookSpecificOutput" => { "hookEventName" => event, "additionalContext" => lines.join("\n") } }
62
+ Lines.new(stdout: "#{JSON.generate(output)}\n", stderr: "", status: 0)
63
+ end
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,56 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ require_relative "../prose_list"
5
+
6
+ module Principles
7
+ # What this tool prints, from plain values a controller hands it. views/CLAUDE.md owns the boundary.
8
+ module Views
9
+ # One principle's members with no baseline, as list-member-problems' next_action says what gives them one:
10
+ # outside git a completed audit, and in git committing the files that hold them (#564).
11
+ class MembersWithNoBaseline
12
+ # because is "outside_git" or "not_committed"; audit is the start-audit command to run outside git, and
13
+ # files the map and member files to commit in git.
14
+ #: (id: String, count: Integer, because: String, audit: String, files: Array[String]) -> void
15
+ def initialize(id:, count:, because:, audit:, files:)
16
+ @id = id
17
+ @count = count
18
+ @because = because
19
+ @audit = audit
20
+ @files = files
21
+ freeze
22
+ end
23
+
24
+ #: () -> String
25
+ def to_s
26
+ "#{@count} member#{"s" unless one?} of #{@id} #{one? ? "has" : "have"} no baseline, so whether " \
27
+ "#{one? ? "it" : "they"} changed is not yet known.#{remedy}"
28
+ end
29
+
30
+ private
31
+
32
+ #: () -> bool
33
+ def one? = @count == 1
34
+
35
+ # What gives the members a baseline: a completed audit outside git, or in git committing the files that
36
+ # differ from HEAD, or an audit again when none does, since every file is committed already (#710 X3). The
37
+ # audit command starts a line of its own, after its purpose.
38
+ #: () -> String
39
+ def remedy
40
+ return " Committing #{ProseList.new(@files)} #{gives}." if @because == "not_committed" && !@files.empty?
41
+
42
+ "#{all_committed if @because == "not_committed"}\nA completed audit #{gives}: #{@audit}"
43
+ end
44
+
45
+ #: () -> String
46
+ def gives = "gives #{one? ? "it" : "each"} one"
47
+
48
+ #: () -> String
49
+ def all_committed
50
+ them, their, were = one? ? %w[it its was] : %w[them their were]
51
+ " Every file holding #{them} is already committed, but no commit held #{them} where #{their} map places " \
52
+ "#{them} when #{one? ? "it" : "they"} #{were} mapped."
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,23 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ require_relative "prompts"
5
+
6
+ module Principles
7
+ # What this tool prints, from plain values a controller hands it. views/CLAUDE.md owns the boundary.
8
+ module Views
9
+ # The passage setup proposes for every entry file, as the agent pastes it, its lines kept: one or two lines,
10
+ # worded for a reader who may not have principles installed (D68, D71).
11
+ class MetaPrinciplePassage
12
+ #: (heading: String) -> void
13
+ def initialize(heading:)
14
+ @heading = heading
15
+ freeze
16
+ end
17
+
18
+ # The passage, ending in a line break.
19
+ #: () -> String
20
+ def text = "#{Prompts.markdown("set-principles-up/entry-file-passage", heading: @heading)}\n"
21
+ end
22
+ end
23
+ end