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.
- checksums.yaml +7 -0
- data/exe/principles +10 -0
- data/lib/principles/after_commit_hook_options.rb +44 -0
- data/lib/principles/cli.rb +149 -0
- data/lib/principles/command_line.rb +188 -0
- data/lib/principles/command_routes.rb +60 -0
- data/lib/principles/controllers/acknowledgement.rb +106 -0
- data/lib/principles/controllers/answer.rb +172 -0
- data/lib/principles/controllers/audit.rb +203 -0
- data/lib/principles/controllers/audit_completion.rb +113 -0
- data/lib/principles/controllers/command.rb +201 -0
- data/lib/principles/controllers/correction.rb +99 -0
- data/lib/principles/controllers/help.rb +107 -0
- data/lib/principles/controllers/inventory.rb +181 -0
- data/lib/principles/controllers/mapping.rb +56 -0
- data/lib/principles/controllers/member_entries.rb +149 -0
- data/lib/principles/controllers/members_a_commit_changed.rb +43 -0
- data/lib/principles/controllers/members_to_assess.rb +160 -0
- data/lib/principles/controllers/meta_principle_setup.rb +92 -0
- data/lib/principles/controllers/repoint.rb +68 -0
- data/lib/principles/controllers/running_tool.rb +41 -0
- data/lib/principles/data/audit_files.rb +96 -0
- data/lib/principles/data/bounded_file.rb +45 -0
- data/lib/principles/data/candidate_files_found.rb +105 -0
- data/lib/principles/data/command-steps.json +305 -0
- data/lib/principles/data/command_steps_file.rb +50 -0
- data/lib/principles/data/expected_type.rb +71 -0
- data/lib/principles/data/ignored_by_git.rb +54 -0
- data/lib/principles/data/json_text.rb +57 -0
- data/lib/principles/data/known_fields.rb +42 -0
- data/lib/principles/data/linked_path.rb +102 -0
- data/lib/principles/data/map_files.rb +104 -0
- data/lib/principles/data/principle_id.rb +32 -0
- data/lib/principles/data/principles_subfolder.rb +93 -0
- data/lib/principles/data/project_files.rb +109 -0
- data/lib/principles/data/repository.rb +165 -0
- data/lib/principles/data/running_version.rb +44 -0
- data/lib/principles/data/schema_check.rb +139 -0
- data/lib/principles/data/schemas/acknowledgement-answer.schema.json +25 -0
- data/lib/principles/data/schemas/answer-guide.schema.json +99 -0
- data/lib/principles/data/schemas/assessment-answer.schema.json +332 -0
- data/lib/principles/data/schemas/audit-record.schema.json +277 -0
- data/lib/principles/data/schemas/command-steps.schema.json +83 -0
- data/lib/principles/data/schemas/correction-answer-guide.schema.json +68 -0
- data/lib/principles/data/schemas/correction-answer.schema.json +306 -0
- data/lib/principles/data/schemas/member-map.schema.json +39 -0
- data/lib/principles/data/schemas/repointing-answer.schema.json +44 -0
- data/lib/principles/error.rb +102 -0
- data/lib/principles/invocation_options.rb +77 -0
- data/lib/principles/models/accepted_loading.rb +36 -0
- data/lib/principles/models/acknowledgement.rb +203 -0
- data/lib/principles/models/activation.rb +64 -0
- data/lib/principles/models/agent_loads.rb +64 -0
- data/lib/principles/models/answer_fields.rb +67 -0
- data/lib/principles/models/answer_place.rb +53 -0
- data/lib/principles/models/answer_text.rb +37 -0
- data/lib/principles/models/answered_time.rb +65 -0
- data/lib/principles/models/assessment_answer.rb +131 -0
- data/lib/principles/models/assessment_basis.rb +47 -0
- data/lib/principles/models/assessment_report.rb +62 -0
- data/lib/principles/models/audit.rb +200 -0
- data/lib/principles/models/audit_record.rb +249 -0
- data/lib/principles/models/audit_records.rb +103 -0
- data/lib/principles/models/audit_start.rb +96 -0
- data/lib/principles/models/audited_member_lines.rb +45 -0
- data/lib/principles/models/candidate_files.rb +66 -0
- data/lib/principles/models/candidate_inventory.rb +67 -0
- data/lib/principles/models/candidate_listing.rb +32 -0
- data/lib/principles/models/carried_assessments.rb +76 -0
- data/lib/principles/models/check_since_last_audit.rb +61 -0
- data/lib/principles/models/cited_files.rb +39 -0
- data/lib/principles/models/command_steps.rb +270 -0
- data/lib/principles/models/commits_brought_in.rb +27 -0
- data/lib/principles/models/commits_since_base.rb +32 -0
- data/lib/principles/models/correction_answer.rb +66 -0
- data/lib/principles/models/correction_basis.rb +70 -0
- data/lib/principles/models/dated_line.rb +29 -0
- data/lib/principles/models/decided_prior_findings.rb +106 -0
- data/lib/principles/models/declared_map_changes.rb +156 -0
- data/lib/principles/models/disposition.rb +233 -0
- data/lib/principles/models/entry_file.rb +65 -0
- data/lib/principles/models/entry_files.rb +46 -0
- data/lib/principles/models/evidence_item.rb +48 -0
- data/lib/principles/models/evidence_lines_since.rb +57 -0
- data/lib/principles/models/evidence_replacements.rb +99 -0
- data/lib/principles/models/finding_write_ups.rb +108 -0
- data/lib/principles/models/findings_left_open.rb +34 -0
- data/lib/principles/models/first_raised_claim.rb +128 -0
- data/lib/principles/models/frontmatter_fields.rb +128 -0
- data/lib/principles/models/hook_commands.rb +100 -0
- data/lib/principles/models/ignored_candidates.rb +46 -0
- data/lib/principles/models/json_digest.rb +35 -0
- data/lib/principles/models/json_path_query.rb +131 -0
- data/lib/principles/models/json_path_selectors.rb +64 -0
- data/lib/principles/models/json_path_string.rb +76 -0
- data/lib/principles/models/kept_prior_finding.rb +70 -0
- data/lib/principles/models/last_completed_revision.rb +70 -0
- data/lib/principles/models/line_span.rb +49 -0
- data/lib/principles/models/loaded_files.rb +60 -0
- data/lib/principles/models/loading_decision.rb +45 -0
- data/lib/principles/models/loading_facts.rb +45 -0
- data/lib/principles/models/loads_to_check.rb +93 -0
- data/lib/principles/models/locate_refusal.rb +23 -0
- data/lib/principles/models/located_text.rb +43 -0
- data/lib/principles/models/locator.rb +52 -0
- data/lib/principles/models/map_members.rb +171 -0
- data/lib/principles/models/markdown_file.rb +92 -0
- data/lib/principles/models/member.rb +171 -0
- data/lib/principles/models/member_assessments.rb +144 -0
- data/lib/principles/models/member_elsewhere.rb +74 -0
- data/lib/principles/models/member_file.rb +156 -0
- data/lib/principles/models/member_grounds.rb +32 -0
- data/lib/principles/models/member_history.rb +68 -0
- data/lib/principles/models/member_lines.rb +95 -0
- data/lib/principles/models/member_loads.rb +197 -0
- data/lib/principles/models/member_map.rb +219 -0
- data/lib/principles/models/member_map_history.rb +143 -0
- data/lib/principles/models/member_maps.rb +185 -0
- data/lib/principles/models/member_moves.rb +95 -0
- data/lib/principles/models/member_problems.rb +253 -0
- data/lib/principles/models/member_sighting.rb +176 -0
- data/lib/principles/models/member_snapshots.rb +36 -0
- data/lib/principles/models/member_state.rb +35 -0
- data/lib/principles/models/members_elsewhere.rb +46 -0
- data/lib/principles/models/members_sharing_lines.rb +116 -0
- data/lib/principles/models/meta_principle.rb +51 -0
- data/lib/principles/models/missing_files.rb +85 -0
- data/lib/principles/models/misspelt_member_id.rb +45 -0
- data/lib/principles/models/open_finding.rb +221 -0
- data/lib/principles/models/open_findings_elsewhere.rb +45 -0
- data/lib/principles/models/other_principles.rb +47 -0
- data/lib/principles/models/placement.rb +42 -0
- data/lib/principles/models/plain_text_file.rb +32 -0
- data/lib/principles/models/principles_folder_move.rb +59 -0
- data/lib/principles/models/principles_package.rb +53 -0
- data/lib/principles/models/prior_finding_decision.rb +162 -0
- data/lib/principles/models/prior_finding_outcomes.rb +60 -0
- data/lib/principles/models/prior_findings.rb +109 -0
- data/lib/principles/models/problem_report.rb +110 -0
- data/lib/principles/models/project.rb +214 -0
- data/lib/principles/models/project_history.rb +203 -0
- data/lib/principles/models/record_judged_against.rb +42 -0
- data/lib/principles/models/recorded_entry.rb +93 -0
- data/lib/principles/models/recorded_finding.rb +97 -0
- data/lib/principles/models/recorded_findings.rb +58 -0
- data/lib/principles/models/recorded_members.rb +149 -0
- data/lib/principles/models/repoint.rb +112 -0
- data/lib/principles/models/reported_check.rb +130 -0
- data/lib/principles/models/request_id.rb +70 -0
- data/lib/principles/models/runs_from.rb +36 -0
- data/lib/principles/models/skill_loads.rb +123 -0
- data/lib/principles/models/still_holds_restatement.rb +87 -0
- data/lib/principles/models/text_size.rb +26 -0
- data/lib/principles/models/uncommitted_outside_the_map.rb +30 -0
- data/lib/principles/models/waits_on.rb +58 -0
- data/lib/principles/models/write_up_opening.rb +93 -0
- data/lib/principles/models/write_up_pointer.rb +50 -0
- data/lib/principles/models/write_up_repoint.rb +39 -0
- data/lib/principles/prose_list.rb +23 -0
- data/lib/principles/supported_ruby.rb +93 -0
- data/lib/principles/version.rb +11 -0
- data/lib/principles/views/acknowledged_since_last_audit.rb +34 -0
- data/lib/principles/views/acknowledgement_result.rb +44 -0
- data/lib/principles/views/activation_in_hand.rb +47 -0
- data/lib/principles/views/assessment_request.rb +131 -0
- data/lib/principles/views/audit_changes.rb +79 -0
- data/lib/principles/views/audit_commit.rb +55 -0
- data/lib/principles/views/audit_result.rb +147 -0
- data/lib/principles/views/candidate_files_result.rb +95 -0
- data/lib/principles/views/check_result_template.rb +75 -0
- data/lib/principles/views/diff_since_last_audit.rb +29 -0
- data/lib/principles/views/findings_result.rb +45 -0
- data/lib/principles/views/help.rb +119 -0
- data/lib/principles/views/invocation.rb +66 -0
- data/lib/principles/views/landed_since_base.rb +27 -0
- data/lib/principles/views/lines_shared_with_member.rb +111 -0
- data/lib/principles/views/loads_to_check_result.rb +45 -0
- data/lib/principles/views/map_result.rb +169 -0
- data/lib/principles/views/member_change_since_baseline.rb +58 -0
- data/lib/principles/views/member_repairs.rb +82 -0
- data/lib/principles/views/members_a_commit_changed.rb +66 -0
- data/lib/principles/views/members_with_no_baseline.rb +56 -0
- data/lib/principles/views/meta_principle_passage.rb +23 -0
- data/lib/principles/views/meta_principle_result.rb +170 -0
- data/lib/principles/views/open_findings_elsewhere_result.rb +32 -0
- data/lib/principles/views/open_findings_line.rb +125 -0
- data/lib/principles/views/other_principles_unjudged_result.rb +32 -0
- data/lib/principles/views/outside_the_map_result.rb +31 -0
- data/lib/principles/views/package_since_last_audit_result.rb +34 -0
- data/lib/principles/views/principle_read_result.rb +86 -0
- data/lib/principles/views/principle_with_changed_members.rb +26 -0
- data/lib/principles/views/principles_with_no_completed_audit.rb +30 -0
- data/lib/principles/views/printed_principle.rb +45 -0
- data/lib/principles/views/prior_finding_decisions_result.rb +35 -0
- data/lib/principles/views/prior_findings_result.rb +80 -0
- data/lib/principles/views/problem_lines.rb +84 -0
- data/lib/principles/views/problem_result.rb +107 -0
- data/lib/principles/views/prompts/acknowledge-a-change/say-whether-the-change-keeps-the-meaning.md +12 -0
- data/lib/principles/views/prompts/answer-any-judgement-request/what-a-self-audit-cannot-show.md +6 -0
- data/lib/principles/views/prompts/assess-every-member/answer-guide.json +63 -0
- data/lib/principles/views/prompts/assess-every-member/assess-each-member-and-the-set.md +12 -0
- data/lib/principles/views/prompts/assess-every-member/check-roles-other-principles-give-these-lines.md +7 -0
- data/lib/principles/views/prompts/assess-every-member/decide-each-member-loading.md +17 -0
- data/lib/principles/views/prompts/assess-every-member/decide-each-prior-finding.md +38 -0
- data/lib/principles/views/prompts/assess-every-member/give-problem-evidence-at-still-holds.md +2 -0
- data/lib/principles/views/prompts/assess-every-member/give-the-evidence-a-prior-finding-lacks.md +7 -0
- data/lib/principles/views/prompts/assess-every-member/have-a-changed-check-reviewed-before-running-it.md +4 -0
- data/lib/principles/views/prompts/assess-every-member/have-a-new-check-reviewed-before-running-it.md +3 -0
- data/lib/principles/views/prompts/assess-every-member/have-hidden-characters-in-a-check-explained.md +4 -0
- data/lib/principles/views/prompts/assess-every-member/judge-acknowledged-edits-together.md +6 -0
- data/lib/principles/views/prompts/assess-every-member/judge-the-keeping-against-the-other-principles.md +7 -0
- data/lib/principles/views/prompts/assess-every-member/judge-the-keeping-under-the-release-since-the-last-audit.md +10 -0
- data/lib/principles/views/prompts/assess-every-member/judge-the-set-with-carried-assessments.md +10 -0
- data/lib/principles/views/prompts/assess-every-member/keep-the-last-review-basis-unless-you-give-a-new-one.md +1 -0
- data/lib/principles/views/prompts/assess-every-member/name-another-principles-open-finding-rather-than-raise-it.md +9 -0
- data/lib/principles/views/prompts/assess-every-member/read-whether-each-prior-finding-s-evidence-changed.md +12 -0
- data/lib/principles/views/prompts/assess-every-member/run-a-named-check-at-every-full-audit.md +2 -0
- data/lib/principles/views/prompts/assess-every-member/run-the-check-then-report-it.md +2 -0
- data/lib/principles/views/prompts/assess-every-member/run-the-reviewed-check-and-say-what-its-review-rested-on.md +6 -0
- data/lib/principles/views/prompts/assess-every-member/uncommitted-files-outside-the-map.md +4 -0
- data/lib/principles/views/prompts/assess-every-member/why-each-member-is-judged-again.md +24 -0
- data/lib/principles/views/prompts/dispose-of-each-finding/answer-guide.json +34 -0
- data/lib/principles/views/prompts/dispose-of-each-finding/fix-or-record-each-finding.md +24 -0
- data/lib/principles/views/prompts/find-a-principle-to-map/ask-the-owner-before-mapping.md +1 -0
- data/lib/principles/views/prompts/find-a-principle-to-map/choose-one-principle.md +5 -0
- data/lib/principles/views/prompts/find-a-principle-to-map/owner-ask-ignored-folders.md +2 -0
- data/lib/principles/views/prompts/find-a-principle-to-map/owner-ask-ignored.md +3 -0
- data/lib/principles/views/prompts/find-a-principle-to-map/owner-ask-possibly-test-data.md +2 -0
- data/lib/principles/views/prompts/find-a-principle-to-map/owner-ask-untracked.md +2 -0
- data/lib/principles/views/prompts/find-a-principle-to-map/what-each-command-writes.md +4 -0
- data/lib/principles/views/prompts/find-a-principle-to-map/where-the-inventory-looks.md +10 -0
- data/lib/principles/views/prompts/learn-what-a-command-does/add-member.md +45 -0
- data/lib/principles/views/prompts/learn-what-a-command-does/change-activation.md +21 -0
- data/lib/principles/views/prompts/learn-what-a-command-does/change-description.md +2 -0
- data/lib/principles/views/prompts/learn-what-a-command-does/change-member-check.md +17 -0
- data/lib/principles/views/prompts/learn-what-a-command-does/change-member-role.md +9 -0
- data/lib/principles/views/prompts/learn-what-a-command-does/create-principle.md +6 -0
- data/lib/principles/views/prompts/learn-what-a-command-does/every-command-and-how-they-chain.md +28 -0
- data/lib/principles/views/prompts/learn-what-a-command-does/list-candidate-files.md +17 -0
- data/lib/principles/views/prompts/learn-what-a-command-does/list-member-problems.md +73 -0
- data/lib/principles/views/prompts/learn-what-a-command-does/list-open-findings.md +71 -0
- data/lib/principles/views/prompts/learn-what-a-command-does/list-principles.md +7 -0
- data/lib/principles/views/prompts/learn-what-a-command-does/move-member.md +11 -0
- data/lib/principles/views/prompts/learn-what-a-command-does/remove-member-check.md +10 -0
- data/lib/principles/views/prompts/learn-what-a-command-does/remove-member.md +6 -0
- data/lib/principles/views/prompts/learn-what-a-command-does/show-answer-check.md +16 -0
- data/lib/principles/views/prompts/learn-what-a-command-does/show-audit.md +20 -0
- data/lib/principles/views/prompts/learn-what-a-command-does/show-meta-principle-passage.md +52 -0
- data/lib/principles/views/prompts/learn-what-a-command-does/show-principle.md +86 -0
- data/lib/principles/views/prompts/learn-what-a-command-does/start-audit.md +68 -0
- data/lib/principles/views/prompts/learn-what-a-command-does/submit-answer.md +56 -0
- data/lib/principles/views/prompts/repoint-write-ups/point-each-moved-write-up-at-its-new-place.md +10 -0
- data/lib/principles/views/prompts/report-after-a-commit/it-refused-after-the-commits-touched-the-maps.md +2 -0
- data/lib/principles/views/prompts/report-after-a-commit/others-are-still-listed.md +1 -0
- data/lib/principles/views/prompts/report-after-a-commit/the-commits-changed-these-members.md +2 -0
- data/lib/principles/views/prompts/report-after-a-commit/this-command-shows-each-one.md +1 -0
- data/lib/principles/views/prompts/report-the-completed-audit/commit-only-what-the-audit-changed.md +2 -0
- data/lib/principles/views/prompts/report-the-completed-audit/replace-the-default-commit-message.md +4 -0
- data/lib/principles/views/prompts/report-the-completed-audit/what-completion-means.md +2 -0
- data/lib/principles/views/prompts/set-principles-up/entry-file-passage.md +3 -0
- data/lib/principles/views/prompts/set-principles-up/name-the-step-each-member-lacks.md +15 -0
- data/lib/principles/views/prompts/set-principles-up/place-the-passage-with-the-user.md +31 -0
- data/lib/principles/views/prompts/set-principles-up/set-up-first.md +2 -0
- data/lib/principles/views/prompts/set-principles-up/what-the-passage-presumes.md +4 -0
- data/lib/principles/views/prompts/start-a-session/act-on-the-open-findings.md +5 -0
- data/lib/principles/views/prompts/start-a-session/it-returns-after-each-clear-or-compaction-too.md +1 -0
- data/lib/principles/views/prompts/start-a-session/it-returns-at-each-session-start.md +1 -0
- data/lib/principles/views/prompts/start-a-session/raise-them-at-the-next-pause.md +1 -0
- data/lib/principles/views/prompts/start-a-session/raise-them-before-the-first-task.md +1 -0
- data/lib/principles/views/prompts.rb +71 -0
- data/lib/principles/views/repoint_result.rb +80 -0
- data/lib/principles/views/request_part.rb +26 -0
- data/lib/principles/views/request_parts.rb +26 -0
- data/lib/principles/views/shell_word.rb +29 -0
- data/lib/principles/views/visible_text.rb +26 -0
- data/lib/principles/views/workflow_diagram.rb +77 -0
- data/lib/principles.rb +5 -0
- metadata +326 -0
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
# typed: strict
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require "shellwords"
|
|
5
|
+
require_relative "../error"
|
|
6
|
+
require_relative "../models/answer_text"
|
|
7
|
+
require_relative "../models/audit"
|
|
8
|
+
require_relative "../models/carried_assessments"
|
|
9
|
+
require_relative "../models/member"
|
|
10
|
+
require_relative "../models/member_grounds"
|
|
11
|
+
require_relative "../models/member_map_history"
|
|
12
|
+
require_relative "../models/members_sharing_lines"
|
|
13
|
+
require_relative "../models/project"
|
|
14
|
+
require_relative "../models/record_judged_against"
|
|
15
|
+
require_relative "../views/audit_result"
|
|
16
|
+
require_relative "../views/lines_shared_with_member"
|
|
17
|
+
require_relative "../views/map_result"
|
|
18
|
+
require_relative "../views/member_repairs"
|
|
19
|
+
require_relative "running_tool"
|
|
20
|
+
|
|
21
|
+
module Principles
|
|
22
|
+
# Each runs one command: reads models, applies their rules and prints through a view.
|
|
23
|
+
# controllers/CLAUDE.md owns the boundary.
|
|
24
|
+
module Controllers
|
|
25
|
+
# What every command controller shares: the project and its models, the views that print, and the
|
|
26
|
+
# running executable, which only a controller knows. Reads change nothing on disk, map writes touch only
|
|
27
|
+
# the committed map, and audit writes only the committed audit record; lib/principles/CLAUDE.md owns the
|
|
28
|
+
# naming rule that keeps the three apart.
|
|
29
|
+
class Command
|
|
30
|
+
# The command as the agent can run it from a checkout or an installed gem: this Ruby and this checkout's
|
|
31
|
+
# executable, because this tool is not on the PATH there. The path is shell-quoted, since an install
|
|
32
|
+
# directory may hold a space. An entry point that is on the PATH names itself instead.
|
|
33
|
+
INVOCATION = "ruby #{Shellwords.escape(File.expand_path("../../../exe/principles", __dir__))}".freeze #: String
|
|
34
|
+
|
|
35
|
+
# tool is this run of the tool: the executable every printed command names, and the version running.
|
|
36
|
+
#: (Models::Project, tool: RunningTool) -> void
|
|
37
|
+
def initialize(project, tool:)
|
|
38
|
+
@project = project
|
|
39
|
+
@tool = tool
|
|
40
|
+
@member_maps = @project.member_maps #: Models::MemberMaps
|
|
41
|
+
@audit_records = @project.audit_records #: Models::AuditRecords
|
|
42
|
+
@history = @project.history #: Models::ProjectHistory
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# Runs command; each controller runs its own group's commands and refuses the rest.
|
|
46
|
+
#: (String, String?, Hash[String, untyped]) -> untyped
|
|
47
|
+
def call(command, _id, _options)
|
|
48
|
+
naming_the_executable { unknown(command) }
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
private
|
|
52
|
+
|
|
53
|
+
# The history of principle_id's map, read through the collaborators this controller already holds.
|
|
54
|
+
#: (String) -> Models::MemberMapHistory
|
|
55
|
+
def map_history(principle_id)
|
|
56
|
+
Models::MemberMapHistory.new(id: principle_id, history: @history, member_maps: @member_maps)
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# Saves the map, and nothing else, then prints the result the block builds from the map as written.
|
|
60
|
+
#: (Models::MemberMap) { (Views::PrintedPrinciple) -> Hash[String, untyped] } -> untyped
|
|
61
|
+
def write_map(map, &)
|
|
62
|
+
wrote = @member_maps.save(map)
|
|
63
|
+
yield(printed_principle(map)).merge("wrote" => [wrote])
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
# Runs the block, putting the running executable into any refusal a model raised, followed by this project,
|
|
67
|
+
# so a repair runs on the same project from anywhere, as answer_with does (#572). rerun, when given, prints the
|
|
68
|
+
# command running now with another member id, which a refusal naming a near miss leads its repair with (D56).
|
|
69
|
+
#: [T] (?rerun: (^(String) -> String)?) { () -> T } -> T
|
|
70
|
+
def naming_the_executable(rerun: nil, &)
|
|
71
|
+
yield
|
|
72
|
+
rescue Error => e
|
|
73
|
+
refusal = (rerun ? e.rerun_with_near_miss(&rerun) : e).printing_commands do |command|
|
|
74
|
+
invocation.listed_in_project(command.command, command.values, options: command.options, usage: command.usage)
|
|
75
|
+
end
|
|
76
|
+
raise with_member_repairs(refusal).naming("#{@tool.executable} #{invocation.project_option}")
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
# A broken_members refusal, with each member's own repair commands beside it as repair_with, since a
|
|
80
|
+
# model's repair can only name placeholders (#252). Any other refusal is returned as it is.
|
|
81
|
+
#: (Error) -> Error
|
|
82
|
+
def with_member_repairs(error)
|
|
83
|
+
return error unless error.payload["code"] == "broken_members"
|
|
84
|
+
|
|
85
|
+
repairs = Views::MemberRepairs.new(invocation)
|
|
86
|
+
members = error.payload.fetch("members").map do |entry|
|
|
87
|
+
repaired({ "principle" => error.payload["principle"] }.compact.merge(entry), repairs)
|
|
88
|
+
end
|
|
89
|
+
error.including("members" => members)
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
# entry with repair_with, when it names a member its map still lists.
|
|
93
|
+
#: (Hash[String, untyped], Views::MemberRepairs) -> Hash[String, untyped]
|
|
94
|
+
def repaired(entry, repairs)
|
|
95
|
+
principle = entry["principle"] or return entry
|
|
96
|
+
id = entry["id"] or return entry
|
|
97
|
+
fields = @member_maps.find(principle).member(id).merge(entry.slice("code")).merge("id" => id)
|
|
98
|
+
entry.merge("repair_with" => repairs.commands(principle, fields))
|
|
99
|
+
rescue Error
|
|
100
|
+
entry
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
#: (String) -> bot
|
|
104
|
+
def unknown(command)
|
|
105
|
+
raise Error.new("unknown_command", "Unknown command #{command}",
|
|
106
|
+
"To see every command: #{@tool.executable} --help")
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
# The controller this one hands part of its command to, over the same project and naming the same executable.
|
|
110
|
+
#: [C < Command] (Class[C], **untyped) -> C
|
|
111
|
+
def handed(controller, **)
|
|
112
|
+
controller.new(@project, tool: @tool, **)
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
# The executable and project that printed commands name, built for each run and never stored.
|
|
116
|
+
#: () -> Views::Invocation
|
|
117
|
+
def invocation
|
|
118
|
+
Views::Invocation.new(executable: @tool.executable, project: @project.root)
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
#: () -> Views::AuditResult
|
|
122
|
+
def audit_result
|
|
123
|
+
Views::AuditResult.new(invocation)
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
#: () -> Views::MapResult
|
|
127
|
+
def map_result
|
|
128
|
+
Views::MapResult.new(invocation, in_git: @history.inside_git?)
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
#: (String?) -> String
|
|
132
|
+
def required_id(id)
|
|
133
|
+
unless id
|
|
134
|
+
raise Error.new("missing_id", "An id is required",
|
|
135
|
+
"To see each command's examples: #{@tool.executable} --help")
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
id
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
#: (untyped, String) -> String
|
|
142
|
+
def text(value, field) = Models::AnswerText.new(value).required(field)
|
|
143
|
+
|
|
144
|
+
# The principle as the view prints it, with each member's id beside its locator.
|
|
145
|
+
#: (Models::MemberMap, ?members: Array[Hash[String, untyped]]) -> Views::PrintedPrinciple
|
|
146
|
+
def printed_principle(map, members: map.members_shown)
|
|
147
|
+
Views::PrintedPrinciple.new(id: map.id, file: @member_maps.file(map.id), description: map.description,
|
|
148
|
+
members: members, activation: map.activation)
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
# Each member's hash at base, or nil outside git.
|
|
152
|
+
#: (String?, Models::MemberMap) -> Hash[String, String]?
|
|
153
|
+
def committed(base, map) = base ? @history.hashes_at(base, map.members) : nil
|
|
154
|
+
|
|
155
|
+
# When each of the map's members loads, by id, which a full audit's request lists against the activation and
|
|
156
|
+
# show-principle prints, from the project's hooks as hooks read them.
|
|
157
|
+
#: (Models::MemberMap, ?Models::HookCommands) -> Hash[String, Models::MemberLoads]
|
|
158
|
+
def member_loads(map, hooks = @project.hook_commands) = @project.member_loads(map.members, hooks)
|
|
159
|
+
|
|
160
|
+
# The members a full audit lists to check against its activation, each with the decision its last completed
|
|
161
|
+
# audit, whose record is given or read now, accepted on the member's loading (#355, #502).
|
|
162
|
+
#: (Models::Audit, ?Models::AuditRecord?) -> Models::LoadsToCheck
|
|
163
|
+
def loads_to_check(audit, record = @audit_records.find(audit.id))
|
|
164
|
+
audit.loads_to_check(member_loads(audit.map), record)
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
# The principle's audit record as it stands, as an assessment answer is judged against it: the members whose
|
|
168
|
+
# loads it decides and the assessments it may carry forward, from one read of the record.
|
|
169
|
+
#: (Models::Audit) -> Models::RecordJudgedAgainst
|
|
170
|
+
def record_judged_against(audit)
|
|
171
|
+
record = @audit_records.find(audit.id)
|
|
172
|
+
Models::RecordJudgedAgainst.new(loads: loads_to_check(audit, record),
|
|
173
|
+
carried: carried_assessments(audit, record), record: record)
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
# What each member's assessment was judged on beside its text: its map entry and when it loads (D49).
|
|
177
|
+
#: (Models::Audit) -> Models::MemberGrounds
|
|
178
|
+
def grounds(audit) = audit.grounds(member_loads(audit.map))
|
|
179
|
+
|
|
180
|
+
# Which members' last assessments a full audit carries forward, from its record, given or read now, and the
|
|
181
|
+
# question it asks (D49).
|
|
182
|
+
#: (Models::Audit, ?Models::AuditRecord?) -> Models::CarriedAssessments
|
|
183
|
+
def carried_assessments(audit, record = @audit_records.find(audit.id))
|
|
184
|
+
audit.carried_assessments(record, grounds: grounds(audit), question: Views::AssessmentRequest.question)
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
# Where a member is in its file now, and the hash of what it says there.
|
|
188
|
+
#: (Models::Locator) -> Hash[String, untyped]
|
|
189
|
+
def snapshot(locator)
|
|
190
|
+
@project.locate(locator)
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
# The members sharing a line with member where it is now, in map and in the other maps, as add-member and
|
|
194
|
+
# show-principle print them (#383, #541).
|
|
195
|
+
#: (Models::Member, Models::MemberMap) -> Views::LinesSharedWithMember
|
|
196
|
+
def lines_shared(member, map)
|
|
197
|
+
Views::LinesSharedWithMember.new(**Models::MembersSharingLines.of(@project, member).facts_beside(map))
|
|
198
|
+
end
|
|
199
|
+
end
|
|
200
|
+
end
|
|
201
|
+
end
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
# typed: strict
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require_relative "command"
|
|
5
|
+
require_relative "../models/audit"
|
|
6
|
+
require_relative "../models/correction_answer"
|
|
7
|
+
require_relative "../models/declared_map_changes"
|
|
8
|
+
require_relative "../models/request_id"
|
|
9
|
+
require_relative "../views/prompts"
|
|
10
|
+
require_relative "audit_completion"
|
|
11
|
+
|
|
12
|
+
module Principles
|
|
13
|
+
# Each runs one command: reads models, applies their rules and prints through a view.
|
|
14
|
+
# controllers/CLAUDE.md owns the boundary.
|
|
15
|
+
module Controllers
|
|
16
|
+
# A full audit's correction, which submit-answer and show-answer-check hand over: this tool checks each
|
|
17
|
+
# disposition, and any members moved or whose role or check a fix changed, against the map and hashes the
|
|
18
|
+
# audit began from, and completes the audit (D28, D40).
|
|
19
|
+
class Correction < Command
|
|
20
|
+
# The result of answering a correction request, and the record change it makes, not yet written. A refused
|
|
21
|
+
# answer to a request printed before the record changed is refused as stale, so the agent starts again rather
|
|
22
|
+
# than repairing it (#671).
|
|
23
|
+
#: (Models::RequestId, untyped, Models::Audit) -> [untyped, Array[Models::AuditRecord]]
|
|
24
|
+
def answered(request_id, response, audit)
|
|
25
|
+
Models::CorrectionBasis.new(response.fetch("basis")).printed_after
|
|
26
|
+
.refusing_stale_first(audit.id, @audit_records.find(audit.id)) do
|
|
27
|
+
judged_correction(request_id, response, audit)
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# What each map entry a fix claims changed, from the map the audit the block gives started from and its map
|
|
32
|
+
# now; the audit is built and the base map read only when a fix claims one (#462).
|
|
33
|
+
#: (untyped) { () -> Models::Audit } -> Array[Hash[String, untyped]]
|
|
34
|
+
def map_entry_changes(response, &)
|
|
35
|
+
changes = Models::DeclaredMapChanges.declared_in(response)
|
|
36
|
+
changes.entry_changes do
|
|
37
|
+
audit = yield
|
|
38
|
+
[started_from(Models::CorrectionBasis.new(response.fetch("basis")), audit, changes), audit.map]
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
private
|
|
43
|
+
|
|
44
|
+
# The result of judging a correction answer: its basis, moves and dispositions checked, and the audit completed.
|
|
45
|
+
#: (Models::RequestId, untyped, Models::Audit) -> [untyped, Array[Models::AuditRecord]]
|
|
46
|
+
def judged_correction(request_id, response, audit)
|
|
47
|
+
basis = Models::CorrectionBasis.new(response.fetch("basis"))
|
|
48
|
+
changes = Models::DeclaredMapChanges.declared_in(response)
|
|
49
|
+
moves = Models::MemberMoves.new(response["moved_members"])
|
|
50
|
+
before = moves.hashes(confirmed_start(request_id, basis, audit, changes))
|
|
51
|
+
findings = moves.findings(basis.findings)
|
|
52
|
+
start = audit.resumed(basis, before, moves)
|
|
53
|
+
completion(changes, basis).completed(start, audit, dispositions(response, findings, start, audit), findings,
|
|
54
|
+
basis.prior_decided.moved(moves))
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# The packaged answer guide, which the correction request prints, is the one owner of its samples, the text
|
|
58
|
+
# the agent saw, so its sample for each action is handed to the model, which refuses a field given back as
|
|
59
|
+
# its sample (#789). Should the tool be upgraded between request and answer, that answer is checked only
|
|
60
|
+
# against the new guide's samples.
|
|
61
|
+
#: (untyped, Array[untyped], Models::AuditStart, Models::Audit) -> Array[Models::Disposition]
|
|
62
|
+
def dispositions(response, findings, start, audit)
|
|
63
|
+
samples = Views::Prompts.data("dispose-of-each-finding/answer-guide").fetch("disposition")
|
|
64
|
+
Models::CorrectionAnswer.new(response, findings, samples:) { |locator| snapshot(locator) }
|
|
65
|
+
.dispositions(changed: start.changed(now: audit.now))
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# The map the audit began from, read from the base commit when the answer declares changes to the map.
|
|
69
|
+
#: (Models::CorrectionBasis, Models::Audit, Models::DeclaredMapChanges) -> Models::MemberMap
|
|
70
|
+
def started_from(basis, audit, changes)
|
|
71
|
+
audit.map_started_from(basis, changes, map_file: @member_maps.file(audit.id)) do |base|
|
|
72
|
+
committed_map(base, audit.id)
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
# The map id as the base commit holds it.
|
|
77
|
+
#: (String, String) -> Models::MemberMap
|
|
78
|
+
def committed_map(base, id)
|
|
79
|
+
map_history(id).map_at(base)
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
# Each member's hash when the audit began, checked against the request id: from the map it started from at the
|
|
83
|
+
# base commit in git, after the declared changes are checked against that map, or from the open record outside
|
|
84
|
+
# git.
|
|
85
|
+
#: (Models::RequestId, Models::CorrectionBasis, Models::Audit, Models::DeclaredMapChanges) -> Hash[String, String]
|
|
86
|
+
def confirmed_start(request_id, basis, audit, changes)
|
|
87
|
+
map = started_from(basis, audit, changes)
|
|
88
|
+
base = basis.base_commit
|
|
89
|
+
found = base ? committed(base, map) : @audit_records.find(audit.id)&.base_members
|
|
90
|
+
audit.confirmed_start(request_id, basis, found)
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
#: (Models::DeclaredMapChanges, Models::CorrectionBasis) -> AuditCompletion
|
|
94
|
+
def completion(map_changes, basis)
|
|
95
|
+
handed(AuditCompletion, map_changes: map_changes, replacing: basis.printed_after)
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
end
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
# typed: strict
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require_relative "../models/command_steps"
|
|
5
|
+
require_relative "command"
|
|
6
|
+
require_relative "../views/help"
|
|
7
|
+
require_relative "../views/workflow_diagram"
|
|
8
|
+
|
|
9
|
+
module Principles
|
|
10
|
+
# Each runs one command: reads models, applies their rules and prints through a view.
|
|
11
|
+
# controllers/CLAUDE.md owns the boundary.
|
|
12
|
+
module Controllers
|
|
13
|
+
# --help: reads every command's steps through the model and hands the help view the plain values it prints.
|
|
14
|
+
class Help
|
|
15
|
+
# tool is the running tool. Only the overview reads its version, which the command line reads from the build
|
|
16
|
+
# or the checkout; its executable heads each command's page and starts the command it says to run next. The
|
|
17
|
+
# diagram names neither.
|
|
18
|
+
#: (tool: RunningTool, ?steps: Models::CommandSteps) -> void
|
|
19
|
+
def initialize(tool:, steps: Models::CommandSteps.load)
|
|
20
|
+
@tool = tool
|
|
21
|
+
@steps = steps
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
#: () -> String
|
|
25
|
+
def overview
|
|
26
|
+
groups = @steps.groups.map do |group|
|
|
27
|
+
entries = @steps.commands.select { |command| command.group == group.id }
|
|
28
|
+
.map { |command| [command.usage, command.summary] }
|
|
29
|
+
[group.heading, entries]
|
|
30
|
+
end
|
|
31
|
+
Views::Help::Overview.new(version: @tool.version, groups: groups, executable: @tool.executable).to_s
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# The page for the named command, or nil when there is no such command.
|
|
35
|
+
#: (String) -> String?
|
|
36
|
+
def command(name)
|
|
37
|
+
command = @steps.command(name)
|
|
38
|
+
return unless command
|
|
39
|
+
|
|
40
|
+
Views::Help::CommandPage.new(usage: command.usage, prompt: command.help, writes: @steps.writes(command),
|
|
41
|
+
steps: command.steps.map { |step| page_step(command, step) },
|
|
42
|
+
executable: @tool.executable).to_s
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# The docs page drawing every command's steps and where each leads.
|
|
46
|
+
#: () -> String
|
|
47
|
+
def diagram
|
|
48
|
+
commands = @steps.commands.map do |command|
|
|
49
|
+
nodes = command.steps.map do |step|
|
|
50
|
+
Views::WorkflowDiagram::Node.new(key: key(command, step), label: step.id, agent: step.agent?)
|
|
51
|
+
end
|
|
52
|
+
[command.usage, nodes] #: [String, Array[Views::WorkflowDiagram::Node]]
|
|
53
|
+
end
|
|
54
|
+
Views::WorkflowDiagram.new(commands: commands, edges: @steps.commands.flat_map { |command| edges(command) })
|
|
55
|
+
.to_s
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
private
|
|
59
|
+
|
|
60
|
+
# Each step's edges: to each branch with its condition, to the step after it, or to the first step of
|
|
61
|
+
# the command the agent answers with.
|
|
62
|
+
#: (Models::CommandSteps::Command) -> Array[Views::WorkflowDiagram::Edge]
|
|
63
|
+
def edges(command)
|
|
64
|
+
command.steps.flat_map do |step|
|
|
65
|
+
inside = command.successors(step).each_with_index.map do |successor, index|
|
|
66
|
+
Views::WorkflowDiagram::Edge.new(from: key(command, step), to: key(command, successor),
|
|
67
|
+
label: step.branches[index]&.condition)
|
|
68
|
+
end
|
|
69
|
+
[*inside, *handover(command, step)]
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
#: (Models::CommandSteps::Command, Models::CommandSteps::Step) -> Array[Views::WorkflowDiagram::Edge]
|
|
74
|
+
def handover(command, step)
|
|
75
|
+
target = step.handover.answer_with
|
|
76
|
+
answered = target && @steps.command(target)
|
|
77
|
+
return [] unless target && answered
|
|
78
|
+
|
|
79
|
+
[Views::WorkflowDiagram::Edge.new(from: key(command, step), to: key(answered, answered.steps.fetch(0)),
|
|
80
|
+
label: target, handover: true)]
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
# A step's Mermaid id: its command's and its own position, since step ids repeat across commands.
|
|
84
|
+
#: (Models::CommandSteps::Command, Models::CommandSteps::Step) -> String
|
|
85
|
+
def key(command, step)
|
|
86
|
+
"c#{@steps.commands.index(command)}s#{command.steps.index(step)}"
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
#: (Models::CommandSteps::Command, Models::CommandSteps::Step) -> Views::Help::Step
|
|
90
|
+
def page_step(command, step)
|
|
91
|
+
branches = step.branches.map do |branch|
|
|
92
|
+
[branch.condition, command.steps.index(command.find(branch.to)).to_i + 1] #: [String, Integer]
|
|
93
|
+
end
|
|
94
|
+
Views::Help::Step.new(agent: step.agent?, does: step.does, handover: page_handover(step.handover),
|
|
95
|
+
branches: branches)
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
#: (Models::CommandSteps::Handover) -> Views::Help::Handover
|
|
99
|
+
def page_handover(handover)
|
|
100
|
+
target = handover.answer_with
|
|
101
|
+
Views::Help::Handover.new(checks: handover.checks, cannot_check: handover.cannot_check,
|
|
102
|
+
then_run: handover.answer_usage || (target && @steps.command(target)&.usage),
|
|
103
|
+
then_run_when: handover.answer_when)
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
end
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
# typed: strict
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require_relative "command"
|
|
5
|
+
require_relative "members_a_commit_changed"
|
|
6
|
+
require_relative "../models/member_problems"
|
|
7
|
+
require_relative "../models/member_state"
|
|
8
|
+
require_relative "../models/meta_principle"
|
|
9
|
+
require_relative "../models/problem_report"
|
|
10
|
+
require_relative "../views/problem_result"
|
|
11
|
+
require_relative "../views/problem_lines"
|
|
12
|
+
require_relative "../views/candidate_files_result"
|
|
13
|
+
require_relative "../views/principle_read_result"
|
|
14
|
+
|
|
15
|
+
module Principles
|
|
16
|
+
# Each runs one command: reads models, applies their rules and prints through a view.
|
|
17
|
+
# controllers/CLAUDE.md owns the boundary.
|
|
18
|
+
module Controllers
|
|
19
|
+
# The reads that show what a project holds: its candidate instruction files, its principles, and each
|
|
20
|
+
# member and how fresh it is against the last audit. None changes anything on disk.
|
|
21
|
+
class Inventory < Command
|
|
22
|
+
#: (String, String?, Hash[String, untyped]) -> untyped
|
|
23
|
+
def call(command, id, options)
|
|
24
|
+
naming_the_executable do
|
|
25
|
+
case command
|
|
26
|
+
when "list-candidate-files" then list_candidate_files.merge("wrote" => [])
|
|
27
|
+
when "list-principles" then listing.merge("wrote" => [])
|
|
28
|
+
when "list-member-problems" then problems_printed(id, options)
|
|
29
|
+
when "show-principle" then members(required_id(id)).merge("wrote" => [])
|
|
30
|
+
else unknown(command)
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# Each principle id names as list-principles lists it: its description and its members by freshness, or the
|
|
36
|
+
# refusal that kept its map from being read.
|
|
37
|
+
#: (Array[String]) -> Array[Hash[String, untyped]]
|
|
38
|
+
def rows(ids) = ids.map { entry(it) }
|
|
39
|
+
|
|
40
|
+
private
|
|
41
|
+
|
|
42
|
+
#: () -> untyped
|
|
43
|
+
def list_candidate_files
|
|
44
|
+
found = @project.candidate_files
|
|
45
|
+
listed = Views::CandidateFilesResult::Found.new(inventory: found.to_h, untracked: found.untracked,
|
|
46
|
+
owner_asks: found.owner_asks)
|
|
47
|
+
Views::CandidateFilesResult.new(invocation).question(listed, existing_maps: @member_maps.ids,
|
|
48
|
+
map_file: @member_maps.file("ID"), set_up: set_up?)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
#: (String) -> untyped
|
|
52
|
+
def members(id)
|
|
53
|
+
principle = @member_maps.find(id)
|
|
54
|
+
audit = @audit_records.find(id)
|
|
55
|
+
hooks = @project.hook_commands
|
|
56
|
+
members = shown_members(principle, audit, hooks)
|
|
57
|
+
shown = read_result.shown(printed_principle(principle, members: members),
|
|
58
|
+
broken: broken_ids(members), hook_settings_not_read: hooks.not_read,
|
|
59
|
+
loaded_files: @project.loaded_files(principle.members, hooks).to_h)
|
|
60
|
+
return shown unless audit
|
|
61
|
+
|
|
62
|
+
shown.merge("last_audit" => audit.last_audit(map_version: principle.version))
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# Each member as show-principle prints it, with its loads read once for the whole map, from hooks.
|
|
66
|
+
#: (Models::MemberMap, Models::AuditRecord?, Models::HookCommands) -> Array[Hash[String, untyped]]
|
|
67
|
+
def shown_members(principle, audit, hooks)
|
|
68
|
+
loads = member_loads(principle, hooks)
|
|
69
|
+
principle.members_shown.map do |member|
|
|
70
|
+
shown_member(member, audit, loads.fetch(member.fetch("id"))).merge(shared(member, map: principle))
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
# What show-principle prints of one member: where it is now, its freshness and when its file loads (D38).
|
|
75
|
+
# The span sits beside the id too, as in add-member's and move-member's results (alpha friction).
|
|
76
|
+
#: (Hash[String, untyped], Models::AuditRecord?, Models::MemberLoads) -> Hash[String, untyped]
|
|
77
|
+
def shown_member(member, audit, loads)
|
|
78
|
+
now = fresh(member, audit)
|
|
79
|
+
member.merge(now.fetch("snapshot", {}).slice("first_line", "last_line"), now,
|
|
80
|
+
"loads" => loads.to_h)
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
# The id of each shown member whose reference is broken, so an audit would refuse it, in the map's order.
|
|
84
|
+
#: (Array[Hash[String, untyped]]) -> Array[String]
|
|
85
|
+
def broken_ids(members)
|
|
86
|
+
members.select { |member| Models::MemberState.new(member.fetch("id"), member.fetch("freshness")).broken? }
|
|
87
|
+
.map { |member| member.fetch("id") }
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
# The members sharing a line with this one, in its map and in others, as add-member names them, so a member
|
|
91
|
+
# whose lines others hold too does not look as if its member_bytes were its alone (#541). A member this tool
|
|
92
|
+
# cannot find now shares nothing it can name.
|
|
93
|
+
#: (Hash[String, untyped], map: Models::MemberMap) -> Hash[String, untyped]
|
|
94
|
+
def shared(member, map:)
|
|
95
|
+
lines_shared(Models::Member.new(member), map).to_h
|
|
96
|
+
rescue Error
|
|
97
|
+
{}
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
# Where the member is now and whether it changed since the last audit assessed it.
|
|
101
|
+
#: (Hash[String, untyped], Models::AuditRecord?) -> Hash[String, untyped]
|
|
102
|
+
def fresh(member, audit)
|
|
103
|
+
@project.freshness(Models::Member.new(member).locator) do |sha|
|
|
104
|
+
audit ? audit.members.freshness(Models::Member.new(member).key, sha) : "unassessed"
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
#: () -> Views::PrincipleReadResult
|
|
109
|
+
def read_result
|
|
110
|
+
Views::PrincipleReadResult.new(invocation)
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
#: () -> untyped
|
|
114
|
+
def listing
|
|
115
|
+
read_result.listing(rows(@member_maps.ids), set_up: set_up?)
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
# Whether principles is set up, as MemberMaps decides it (D68, D75).
|
|
119
|
+
#: () -> bool
|
|
120
|
+
def set_up? = @member_maps.set_up?
|
|
121
|
+
|
|
122
|
+
# list-member-problems' JSON result, or with --short its lines for a trigger (#291), or with --hook-event the
|
|
123
|
+
# after-commit hook's JSON, which MembersACommitChanged prints (D73).
|
|
124
|
+
#: (String?, Hash[String, untyped]) -> untyped
|
|
125
|
+
def problems_printed(id, options)
|
|
126
|
+
if (event = options["hook_event"])
|
|
127
|
+
return handed(MembersACommitChanged, report: -> { checked_report(id) })
|
|
128
|
+
.printed(after: options.fetch("commits_after"), event: event)
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
problems = Models::ProblemReport.new(problems_of(id), history: @history)
|
|
132
|
+
report = member_problems(problems)
|
|
133
|
+
return report.merge("wrote" => []) unless options["short"]
|
|
134
|
+
|
|
135
|
+
Views::ProblemLines.new(invocation).short(report, nothing_changed: Views::ProblemResult::NOTHING_CHANGED,
|
|
136
|
+
assessed_change: problems.assessed_change?)
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
# list-member-problems' JSON result for map id, or every map, without the files it wrote.
|
|
140
|
+
#: (String?) -> Hash[String, untyped]
|
|
141
|
+
def checked_report(id) = member_problems(Models::ProblemReport.new(problems_of(id), history: @history))
|
|
142
|
+
|
|
143
|
+
# The result for problems, every member of map id or of every map that is broken or whose own text changed
|
|
144
|
+
# (#187).
|
|
145
|
+
#: (Models::ProblemReport) -> untyped
|
|
146
|
+
def member_problems(problems)
|
|
147
|
+
report = problems.checked do |map_id, member, baseline, now|
|
|
148
|
+
map_history(map_id).member_history(member, baseline: baseline, now: now)
|
|
149
|
+
end
|
|
150
|
+
Views::ProblemResult.new(invocation).checked(report, not_read: @project.unread_older_files,
|
|
151
|
+
no_baseline_files: problems.files_to_commit(@member_maps),
|
|
152
|
+
acknowledgeable: problems.acknowledgeable)
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
# Map id's members against their baselines, or every map's, listing a map it cannot read among them.
|
|
156
|
+
#: (String?) -> Array[Models::MemberProblems]
|
|
157
|
+
def problems_of(id)
|
|
158
|
+
return [Models::MemberProblems.for(@project, id)] if id
|
|
159
|
+
|
|
160
|
+
@member_maps.ids.map { Models::MemberProblems.for(@project, it, listing_all: true) }
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
# One map's row; a map this tool cannot read is listed with its refusal, so the others still show (D29).
|
|
164
|
+
#: (String) -> Hash[String, untyped]
|
|
165
|
+
def entry(id)
|
|
166
|
+
map = @member_maps.find(id)
|
|
167
|
+
row = { "id" => id, "description" => map.description, "members_by_freshness" => members_by_freshness(map) }
|
|
168
|
+
map.unstarted? ? row.merge(read_result.no_members(@member_maps.file(id))) : row
|
|
169
|
+
rescue Error => e
|
|
170
|
+
{ "id" => id, "refusal" => e.payload }
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
# How many of map's members are in each freshness state, against its principle's last audit.
|
|
174
|
+
#: (Models::MemberMap) -> Hash[String, Integer]
|
|
175
|
+
def members_by_freshness(map)
|
|
176
|
+
audit = @audit_records.find(map.id)
|
|
177
|
+
map.members_shown.map { |member| fresh(member, audit).fetch("freshness") }.tally.sort.to_h
|
|
178
|
+
end
|
|
179
|
+
end
|
|
180
|
+
end
|
|
181
|
+
end
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# typed: strict
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require_relative "command"
|
|
5
|
+
require_relative "../models/activation"
|
|
6
|
+
|
|
7
|
+
module Principles
|
|
8
|
+
# Each runs one command: reads models, applies their rules and prints through a view.
|
|
9
|
+
# controllers/CLAUDE.md owns the boundary.
|
|
10
|
+
module Controllers
|
|
11
|
+
# The writes to what a principle declares as a whole in its committed map: that it exists, its
|
|
12
|
+
# description, and when its work is in hand (D38). This tool asks, the agent answers, and this tool writes
|
|
13
|
+
# the map and nothing else. MemberEntries writes each member's entry.
|
|
14
|
+
class Mapping < Command
|
|
15
|
+
# Each command and the method that runs it.
|
|
16
|
+
COMMANDS = {
|
|
17
|
+
"create-principle" => :create_principle, "change-description" => :change_description,
|
|
18
|
+
"change-activation" => :change_activation
|
|
19
|
+
}.freeze #: Hash[String, Symbol]
|
|
20
|
+
|
|
21
|
+
#: (String, String?, Hash[String, untyped]) -> untyped
|
|
22
|
+
def call(command, id, options)
|
|
23
|
+
naming_the_executable do
|
|
24
|
+
method = COMMANDS[command] or unknown(command)
|
|
25
|
+
send(method, required_id(id), options)
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
private
|
|
30
|
+
|
|
31
|
+
#: (String, Hash[String, untyped]) -> untyped
|
|
32
|
+
def create_principle(id, options)
|
|
33
|
+
map = @member_maps.create(id) { text(options["description"], "--description") }
|
|
34
|
+
write_map(map) { |printed| map_result.created(printed) }
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
#: (String, Hash[String, untyped]) -> untyped
|
|
38
|
+
def change_description(id, options)
|
|
39
|
+
map = @member_maps.find(id).describing(text(options["description"], "--description"))
|
|
40
|
+
write_map(map) { |printed| map_result.description_changed(printed) }
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# Records when the principle's work is in hand, as the agent judged it (D38).
|
|
44
|
+
#: (String, Hash[String, untyped]) -> untyped
|
|
45
|
+
def change_activation(id, options)
|
|
46
|
+
activation = Models::Activation.declared(when_files: options["when_files"],
|
|
47
|
+
when_invoked: options["when_invoked"], always: options["always"])
|
|
48
|
+
map = @member_maps.find(id)
|
|
49
|
+
activated = map.activating(activation)
|
|
50
|
+
return map_result.activation_unchanged(printed_principle(map)).merge("wrote" => []) if activated.same_as?(map)
|
|
51
|
+
|
|
52
|
+
write_map(activated) { |printed| map_result.activation_changed(printed, lists: activation.full_audit_lists) }
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|