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,55 @@
|
|
|
1
|
+
# typed: strict
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require_relative "prompts"
|
|
5
|
+
require_relative "../prose_list"
|
|
6
|
+
|
|
7
|
+
module Principles
|
|
8
|
+
# What this tool prints, from plain values a controller hands it. views/CLAUDE.md owns the boundary.
|
|
9
|
+
module Views
|
|
10
|
+
# How a completed audit in git says to commit what it changed: the command committing those files alone, with
|
|
11
|
+
# its default subject, which AuditChanges#commit builds. ByHand is the form outside git, with no command.
|
|
12
|
+
class AuditCommit
|
|
13
|
+
#: (files_to_commit: Array[String], subject: String, command: String) -> void
|
|
14
|
+
def initialize(files_to_commit:, subject:, command:)
|
|
15
|
+
@files_to_commit = files_to_commit
|
|
16
|
+
@subject = subject
|
|
17
|
+
@command = command
|
|
18
|
+
freeze
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# The command, and beside it what replacing its default subject asks, since an agent reading commit_with
|
|
22
|
+
# alone would otherwise run the default (#853).
|
|
23
|
+
#: () -> Hash[String, String]
|
|
24
|
+
def fields
|
|
25
|
+
{ "commit_with" => @command,
|
|
26
|
+
"commit_message_to_replace" => Prompts.text("report-the-completed-audit/replace-the-default-commit-message",
|
|
27
|
+
subject: @subject) }
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# The step that commits the files, the command ending its line (#811), up to the report that follows; its
|
|
31
|
+
# wording is its prompt's. Why the command stops during a merge is its own to say, on stderr (#828).
|
|
32
|
+
#: () -> String
|
|
33
|
+
def next_step
|
|
34
|
+
commit = Prompts.text("report-the-completed-audit/commit-only-what-the-audit-changed",
|
|
35
|
+
files: ProseList.new(@files_to_commit).to_s, command: @command)
|
|
36
|
+
"#{commit}\nThen"
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# Outside git, where no command could run as printed: only the files to commit.
|
|
40
|
+
class ByHand
|
|
41
|
+
#: (Array[String]) -> void
|
|
42
|
+
def initialize(files_to_commit)
|
|
43
|
+
@files_to_commit = files_to_commit
|
|
44
|
+
freeze
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
#: () -> Hash[String, String]
|
|
48
|
+
def fields = {}
|
|
49
|
+
|
|
50
|
+
#: () -> String
|
|
51
|
+
def next_step = "Commit #{ProseList.new(@files_to_commit)}. Then"
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
# typed: strict
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require_relative "assessment_request"
|
|
5
|
+
require_relative "audit_changes"
|
|
6
|
+
require_relative "invocation"
|
|
7
|
+
require_relative "prior_finding_decisions_result"
|
|
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 result of an audit command. Printed commands name the executable and project that are
|
|
14
|
+
# running now, so they are added here each time: a request stays answerable after a move or reinstall
|
|
15
|
+
# (alpha 008 friction 16). A completed result names the files to commit and how to show the record,
|
|
16
|
+
# not what the agent has just written (alpha 008 friction 14, alpha 009 friction 3, alpha 010
|
|
17
|
+
# friction 2).
|
|
18
|
+
class AuditResult
|
|
19
|
+
#: (Invocation) -> void
|
|
20
|
+
def initialize(invocation)
|
|
21
|
+
@invocation = invocation
|
|
22
|
+
freeze
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
#: (Hash[String, untyped]) -> Hash[String, untyped]
|
|
26
|
+
def present(output)
|
|
27
|
+
output["status"] == "agent_judgement_required" ? request(output) : output
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# show-answer-check: the answer passed every check, and what submitting it would write. open_findings_will_print
|
|
31
|
+
# previews each written-up finding's write_up_opening, and is nil when the record would keep none (#411).
|
|
32
|
+
# map_entry_changes gives each claimed map entry's changed role and check, left out when there is none (#462).
|
|
33
|
+
# answer_file is the --file value the answer was checked from, which the submit command gives again (#859).
|
|
34
|
+
#: (String, Array[String], answer_file: String, ?open_findings_will_print: Array[Hash[String, untyped]]?,
|
|
35
|
+
#| ?map_entry_changes: Array[Hash[String, untyped]]) -> Hash[String, untyped]
|
|
36
|
+
def acceptable(request_id, would_write, answer_file:, open_findings_will_print: nil, map_entry_changes: [])
|
|
37
|
+
submit = @invocation.submit_answer(request_id, file: answer_file)
|
|
38
|
+
waits = "Submitting it writes nothing yet: the audit record is written when the audit completes.\n"
|
|
39
|
+
waits = "" unless would_write.empty?
|
|
40
|
+
result = { "status" => "answer_acceptable", "request_id" => request_id, "would_write" => would_write }
|
|
41
|
+
result = result.merge("open_findings_will_print" => open_findings_will_print) if open_findings_will_print
|
|
42
|
+
result = result.merge("map_entry_changes" => map_entry_changes) unless map_entry_changes.empty?
|
|
43
|
+
result.merge("next_action" => "#{waits}Submit the same answer unchanged, as you gave it: #{submit}")
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# start-audit: the assessment request, as a judgement to answer under request_id.
|
|
47
|
+
#: (request_id: String, request: AssessmentRequest) -> Hash[String, untyped]
|
|
48
|
+
def assessment_request(request_id:, request:)
|
|
49
|
+
judgement("assessment", request_id, request.context, request.template)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# The correction request that follows an assessment with findings: one disposition for each, with a guide
|
|
53
|
+
# to every disposition's shape (#414). It says what was stored, so an assessment submitted twice plainly
|
|
54
|
+
# replaced nothing: in git nothing is, and outside git, where basis has no base commit, the open record
|
|
55
|
+
# keeps what the audit began from.
|
|
56
|
+
# placeholders is the text the template leaves in each field the agent writes (#725).
|
|
57
|
+
#: (request_id: String, principle_id: String, findings: Array[untyped], basis: Hash[String, untyped],
|
|
58
|
+
#| placeholders: Hash[String, String]) -> Hash[String, untyped]
|
|
59
|
+
def correction_request(request_id:, principle_id:, findings:, basis:, placeholders:)
|
|
60
|
+
context = { "principle_id" => principle_id, "findings" => findings,
|
|
61
|
+
"question" => Prompts.text("dispose-of-each-finding/fix-or-record-each-finding"),
|
|
62
|
+
"answer_guide" => Prompts.data("dispose-of-each-finding/answer-guide") }
|
|
63
|
+
template = { "basis" => basis, "dispositions" => findings.map do |finding|
|
|
64
|
+
{ "finding_id" => finding["id"], "action" => nil, "reason" => placeholders.fetch("reason"),
|
|
65
|
+
"changed_members" => [], "evidence" => [],
|
|
66
|
+
"reassessment" => placeholders.fetch("reassessment"), "recorded_at" => nil,
|
|
67
|
+
"waits_on" => nil }
|
|
68
|
+
end }
|
|
69
|
+
judgement("correction", request_id, context, template).merge("stored" => stored(basis["base_commit"]))
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
# A completed audit: its resolved and open findings, the files to commit and, in git, each changed
|
|
73
|
+
# member's commits since the base commit and the command that commits those files alone (#811), each prior
|
|
74
|
+
# finding's decision, and the next step. The command's subject states only what the audit did, which the
|
|
75
|
+
# agent rewords to its project's convention.
|
|
76
|
+
#: (principle_id: String, resolved: Array[untyped], open: Array[untyped], changes: AuditChanges,
|
|
77
|
+
#| decisions: PriorFindingDecisionsResult) -> Hash[String, untyped]
|
|
78
|
+
def completion(principle_id:, resolved:, open:, changes:, decisions:)
|
|
79
|
+
changed = changes.to_h
|
|
80
|
+
commit = changes.commit("Audit #{principle_id}: #{resolved.length} resolved, #{open.length} open",
|
|
81
|
+
@invocation.project)
|
|
82
|
+
output = { "status" => "audit_completed", "principle_id" => principle_id, "resolved_findings" => resolved,
|
|
83
|
+
"open_findings" => open, "files_to_commit" => changes.files_to_commit,
|
|
84
|
+
"limits" => Prompts.text("report-the-completed-audit/what-completion-means"),
|
|
85
|
+
**changed.except("files_to_commit"), **commit.fields }
|
|
86
|
+
next_action = completed_next_action(principle_id, changes, commit)
|
|
87
|
+
decisions.added_to(output).merge("next_action" => next_action,
|
|
88
|
+
"full_record" => @invocation.in_project("show-audit", principle_id))
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
# The notice start-audit adds when the last audit's record is not committed.
|
|
92
|
+
#: (String) -> String
|
|
93
|
+
def uncommitted_record_notice(file)
|
|
94
|
+
"The last audit's record, #{file}, is not committed. Commit it, so the project keeps it."
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
private
|
|
98
|
+
|
|
99
|
+
# What submitting the assessment stored, and what submitting it again would do: in git, where the
|
|
100
|
+
# audit has a base commit, nothing; outside git, the open record.
|
|
101
|
+
#: (String?) -> String
|
|
102
|
+
def stored(base_commit)
|
|
103
|
+
again = "Submitting the same assessment again prints this same request"
|
|
104
|
+
unless base_commit
|
|
105
|
+
return "This audit's open record, listed under wrote when it was first written, keeps what the audit " \
|
|
106
|
+
"began from. #{again} and leaves that record as it is."
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
"Nothing is stored yet: this request was computed from your assessment and the project. #{again} and " \
|
|
110
|
+
"replaces nothing; the audit record is written when the audit completes."
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
#: (String, String, Hash[String, untyped], Hash[String, untyped]) -> Hash[String, untyped]
|
|
114
|
+
def judgement(phase, request_id, context, template)
|
|
115
|
+
{ "status" => "agent_judgement_required", "request_id" => request_id, "phase" => phase,
|
|
116
|
+
"context" => context, "response_template" => { "request_id" => request_id }.merge(template),
|
|
117
|
+
"limits" => Prompts.text("answer-any-judgement-request/what-a-self-audit-cannot-show") }
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
#: (Hash[String, untyped]) -> Hash[String, untyped]
|
|
121
|
+
def request(output)
|
|
122
|
+
submit = @invocation.submit_answer(output.fetch("request_id"))
|
|
123
|
+
output.merge("project" => @invocation.project, "answer_with" => submit)
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
# A completed audit's next step, from what it changed and how to commit it.
|
|
127
|
+
#: (String, AuditChanges, AuditCommit | AuditCommit::ByHand) -> String
|
|
128
|
+
def completed_next_action(id, changes, commit)
|
|
129
|
+
"#{commit.next_step} report the outcome to the project's owner.\n" \
|
|
130
|
+
"#{others_changed(changes.other_changed)}" \
|
|
131
|
+
"After members change, audit this principle again: #{@invocation.in_project("start-audit", id)}"
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
# The other principles whose members this audit's fixes changed, each to re-audit, or one member's edit
|
|
135
|
+
# to acknowledge; the agent judges which (#410). Nothing when there are none.
|
|
136
|
+
#: (Array[PrincipleWithChangedMembers]) -> String
|
|
137
|
+
def others_changed(others)
|
|
138
|
+
return "" if others.empty?
|
|
139
|
+
|
|
140
|
+
commands = others.map { |each| "#{@invocation.in_project("start-audit", each.principle_id)}\n" }
|
|
141
|
+
"This audit's fixes also changed or broke members of other principles, listed under " \
|
|
142
|
+
"other_principles_with_changed_members. Repair a broken one from show-principle's repair_with. To " \
|
|
143
|
+
"acknowledge one member's edit instead, add --member MEMBER to its audit. Re-audit each:\n#{commands.join}"
|
|
144
|
+
end
|
|
145
|
+
end
|
|
146
|
+
end
|
|
147
|
+
end
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
# typed: strict
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require_relative "invocation"
|
|
5
|
+
require_relative "prompts"
|
|
6
|
+
|
|
7
|
+
module Principles
|
|
8
|
+
# What this tool prints, from plain values a controller hands it. views/CLAUDE.md owns the boundary.
|
|
9
|
+
module Views
|
|
10
|
+
# list-candidate-files' result: the files that look like agent guidance, and the question of which
|
|
11
|
+
# principle several of them reinforce, asked before anything is mapped.
|
|
12
|
+
class CandidateFilesResult
|
|
13
|
+
# What the search for candidates found, as plain values: the inventory, the files git does not track, nil
|
|
14
|
+
# outside git, where nothing checked them (#386), and the lists to ask the owner about, each with the paths to
|
|
15
|
+
# name in it (#405, #412).
|
|
16
|
+
class Found
|
|
17
|
+
#: Hash[String, Array[String]]
|
|
18
|
+
attr_reader :owner_asks
|
|
19
|
+
|
|
20
|
+
#: (inventory: Hash[String, untyped], untracked: Array[String]?,
|
|
21
|
+
#| owner_asks: Hash[String, Array[String]]) -> void
|
|
22
|
+
def initialize(inventory:, untracked:, owner_asks:)
|
|
23
|
+
@inventory = inventory
|
|
24
|
+
@untracked = untracked
|
|
25
|
+
@owner_asks = owner_asks
|
|
26
|
+
freeze
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# The inventory as printed: each file by path, size and link, where the inventory looks, and untracked only
|
|
30
|
+
# in git.
|
|
31
|
+
#: () -> Hash[String, untyped]
|
|
32
|
+
def listed
|
|
33
|
+
files = @inventory.fetch("files").map { |file| file.slice("path", "characters", "symlink_to") }
|
|
34
|
+
listed = @inventory.merge("files" => files,
|
|
35
|
+
"scope" => Prompts.text("find-a-principle-to-map/where-the-inventory-looks"))
|
|
36
|
+
@untracked ? listed.merge("untracked" => @untracked) : listed
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# The placeholders the printed create-principle command leaves for the agent to fill.
|
|
41
|
+
CREATE_PRINCIPLE_USAGE = "ID --description \"What the principle is and why it matters\""
|
|
42
|
+
|
|
43
|
+
#: (Invocation) -> void
|
|
44
|
+
def initialize(invocation)
|
|
45
|
+
@invocation = invocation
|
|
46
|
+
freeze
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# The inventory, and the question of which principle to map, with one item for each list under found's owner_asks
|
|
50
|
+
# to ask the owner about. Until principles is set_up, next_action puts setting it up first.
|
|
51
|
+
#: (Found, existing_maps: Array[String], map_file: String, set_up: bool) -> Hash[String, untyped]
|
|
52
|
+
def question(found, existing_maps:, map_file:, set_up:)
|
|
53
|
+
{ "status" => "question", "inventory" => found.listed, "existing_maps" => existing_maps,
|
|
54
|
+
"writes" => Prompts.text("find-a-principle-to-map/what-each-command-writes", map_file: map_file),
|
|
55
|
+
"question" => principle_question(found.owner_asks),
|
|
56
|
+
"answer_with" => @invocation.in_project("create-principle", usage: CREATE_PRINCIPLE_USAGE),
|
|
57
|
+
**(set_up ? {} : { "next_action" => set_up_first }) }
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
private
|
|
61
|
+
|
|
62
|
+
# The line that puts setting principles up first, ending in its command.
|
|
63
|
+
#: () -> String
|
|
64
|
+
def set_up_first
|
|
65
|
+
Prompts.text("set-principles-up/set-up-first",
|
|
66
|
+
command: @invocation.in_project("show-meta-principle-passage"))
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# Which principle to map, then one item per list to ask the owner about.
|
|
70
|
+
#: (Hash[String, Array[String]]) -> String
|
|
71
|
+
def principle_question(owner_asks)
|
|
72
|
+
asks = owner_asks.map { |key, paths| "- #{owner_ask(key, named: inline(paths))}" }
|
|
73
|
+
[Prompts.text("find-a-principle-to-map/choose-one-principle"),
|
|
74
|
+
*([Prompts.text("find-a-principle-to-map/ask-the-owner-before-mapping"), *asks].join("\n") if asks.any?)]
|
|
75
|
+
.join("\n\n")
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
# The paths an ask names after its list's name, each quoted, since a path may hold a comma; none, nothing.
|
|
79
|
+
#: (Array[String]) -> String
|
|
80
|
+
def inline(paths) = paths.empty? ? "" : " (#{paths.map { |path| "`#{path}`" }.join(", ")})"
|
|
81
|
+
|
|
82
|
+
# The ask about one inventory list, naming the paths after the list's name.
|
|
83
|
+
#: (String, named: String) -> String
|
|
84
|
+
def owner_ask(key, named:)
|
|
85
|
+
case key
|
|
86
|
+
when "possibly_test_data" then Prompts.text("find-a-principle-to-map/owner-ask-possibly-test-data", named:)
|
|
87
|
+
when "untracked" then Prompts.text("find-a-principle-to-map/owner-ask-untracked", named:)
|
|
88
|
+
when "ignored" then Prompts.text("find-a-principle-to-map/owner-ask-ignored", named:)
|
|
89
|
+
when "ignored_folders" then Prompts.text("find-a-principle-to-map/owner-ask-ignored-folders", named:)
|
|
90
|
+
else raise ArgumentError, "no owner ask for #{key}"
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
end
|
|
@@ -0,0 +1,75 @@
|
|
|
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 check_result a checked member's assessment template starts with: its command, and a not_run_reason
|
|
10
|
+
# that says what to do before replacing it. A check no audit has run, or one changed since, is reviewed
|
|
11
|
+
# before it runs (#227), and any hidden character it holds is named where that decision is made (#260).
|
|
12
|
+
class CheckResultTemplate
|
|
13
|
+
#: (String, Hash[String, untyped]?, ?Array[String]) -> void
|
|
14
|
+
def initialize(check, since_last_audit, hidden_characters = [])
|
|
15
|
+
@check = check
|
|
16
|
+
@since_last_audit = since_last_audit
|
|
17
|
+
@hidden_characters = hidden_characters
|
|
18
|
+
freeze
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
#: () -> Hash[String, untyped]
|
|
22
|
+
def to_h
|
|
23
|
+
{ "command" => @check, "not_run_reason" => [hidden_characters, not_run_reason].compact.join(" ") }
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
private
|
|
27
|
+
|
|
28
|
+
#: () -> String?
|
|
29
|
+
def hidden_characters
|
|
30
|
+
return if @hidden_characters.empty?
|
|
31
|
+
|
|
32
|
+
Prompts.text("assess-every-member/have-hidden-characters-in-a-check-explained",
|
|
33
|
+
characters: @hidden_characters.join("; "))
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# What to do before replacing the field, then that a named check runs at every full audit, which holds for
|
|
37
|
+
# each status.
|
|
38
|
+
#: () -> String
|
|
39
|
+
def not_run_reason
|
|
40
|
+
[*steps, Prompts.text("assess-every-member/run-a-named-check-at-every-full-audit")].join(" ")
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# An unchanged check is run and reported, keeping any line its last review rested on; a new or changed one is
|
|
44
|
+
# reviewed first, its review introduced by what it is, then run and reported with what that review rested on,
|
|
45
|
+
# as both ask the same way.
|
|
46
|
+
#: () -> Array[String]
|
|
47
|
+
def steps
|
|
48
|
+
case @since_last_audit&.fetch("status")
|
|
49
|
+
when "unchanged" then [Prompts.text("assess-every-member/run-the-check-then-report-it"), *kept_review]
|
|
50
|
+
when "changed"
|
|
51
|
+
[Prompts.text("assess-every-member/have-a-changed-check-reviewed-before-running-it"), run_reviewed]
|
|
52
|
+
else [Prompts.text("assess-every-member/have-a-new-check-reviewed-before-running-it"), run_reviewed]
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# That a run keeps the line the last audit's review rested on, quoted with since when, unless a new one is
|
|
57
|
+
# given, so whether it still holds can be judged from the template (#849); a record from before D63 kept none,
|
|
58
|
+
# so there is nothing to say (#826).
|
|
59
|
+
#: () -> Array[String]
|
|
60
|
+
def kept_review
|
|
61
|
+
return [] unless @since_last_audit&.key?("review_basis")
|
|
62
|
+
|
|
63
|
+
[Prompts.text("assess-every-member/keep-the-last-review-basis-unless-you-give-a-new-one",
|
|
64
|
+
since: @since_last_audit.fetch("review_basis_since"),
|
|
65
|
+
basis: @since_last_audit.fetch("review_basis"))]
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# Run a reviewed check, then report it with what its review rested on.
|
|
69
|
+
#: () -> String
|
|
70
|
+
def run_reviewed
|
|
71
|
+
Prompts.text("assess-every-member/run-the-reviewed-check-and-say-what-its-review-rested-on")
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|
|
@@ -0,0 +1,29 @@
|
|
|
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
|
+
# The command that shows the edits to a member's file since the last full audit's base commit, to the files as
|
|
10
|
+
# they are now, which the audit judges. Each word is quoted for the shell, and each path read by git as written,
|
|
11
|
+
# not as a pattern (#558). Renames are followed, so over a moved file's path then and now the diff shows the
|
|
12
|
+
# edit, not a new file (#557).
|
|
13
|
+
class DiffSinceLastAudit
|
|
14
|
+
# last_audited_commit is the last full audit's base commit; paths are the member's file then and now, once each.
|
|
15
|
+
#: (last_audited_commit: String, paths: Array[String]) -> void
|
|
16
|
+
def initialize(last_audited_commit:, paths:)
|
|
17
|
+
@last_audited_commit = last_audited_commit
|
|
18
|
+
@paths = paths
|
|
19
|
+
freeze
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
#: () -> String
|
|
23
|
+
def to_s
|
|
24
|
+
words = [@last_audited_commit, "--", *@paths].map { |word| ShellWord.new(word).to_s }
|
|
25
|
+
"git --literal-pathspecs diff -M #{words.join(" ")}"
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
@@ -0,0 +1,45 @@
|
|
|
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
|
+
# list-open-findings' result: each finding the last completed audit of a principle deferred or accepted, what that
|
|
10
|
+
# covers, and what to do next (#292, D35).
|
|
11
|
+
class FindingsResult
|
|
12
|
+
SCOPE = "Each principle's last completed audit only, as .principles/audits/ID.json holds it. The next full " \
|
|
13
|
+
"audit decides each: one that still holds stays in the record, and one it closes is then only in git: " \
|
|
14
|
+
"git log -p -- .principles/audits/ID.json shows it."
|
|
15
|
+
|
|
16
|
+
NONE_OPEN = "No finding is deferred or accepted in the last completed audit of any principle listed."
|
|
17
|
+
|
|
18
|
+
#: (Invocation) -> void
|
|
19
|
+
def initialize(invocation)
|
|
20
|
+
@invocation = invocation
|
|
21
|
+
freeze
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# findings holds each deferred or accepted finding, with its principle and action.
|
|
25
|
+
#: (Array[Hash[String, untyped]]) -> Hash[String, untyped]
|
|
26
|
+
def open(findings)
|
|
27
|
+
{ "findings" => findings, "scope" => SCOPE, "next_action" => next_action(findings) }
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
private
|
|
31
|
+
|
|
32
|
+
#: (Array[Hash[String, untyped]]) -> String
|
|
33
|
+
def next_action(findings)
|
|
34
|
+
return NONE_OPEN if findings.empty?
|
|
35
|
+
|
|
36
|
+
audits = findings.map { |finding| finding.fetch("principle") }.uniq.map do |id|
|
|
37
|
+
@invocation.in_project("start-audit", id)
|
|
38
|
+
end
|
|
39
|
+
"Read each finding where its recorded_at points; null means it was never written up. A deferral's waits_on " \
|
|
40
|
+
"is whom it was asked to wait on, to decide what, as of waits_on_since: check that it still holds before " \
|
|
41
|
+
"chasing it. Its principle's next full audit asks you to decide each one:\n#{audits.join("\n")}"
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
@@ -0,0 +1,119 @@
|
|
|
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
|
+
# What --help prints: Markdown, because it is a prompt the agent reads before running a command (ticket 001
|
|
10
|
+
# D15). It is kept short, since every line lands in the agent's context. Refusal codes stay out: a refusal
|
|
11
|
+
# arrives with its own code and repair when it happens.
|
|
12
|
+
module Help
|
|
13
|
+
# What the page says after an agent's step: what the tool then checks, what it cannot check, and the
|
|
14
|
+
# usage of the command the agent runs next.
|
|
15
|
+
class Handover
|
|
16
|
+
#: String?
|
|
17
|
+
attr_reader :checks
|
|
18
|
+
#: String?
|
|
19
|
+
attr_reader :cannot_check
|
|
20
|
+
#: String?
|
|
21
|
+
attr_reader :then_run
|
|
22
|
+
# When the agent runs then_run, where it is the agent's to time; "Then" otherwise (#627).
|
|
23
|
+
#: String?
|
|
24
|
+
attr_reader :then_run_when
|
|
25
|
+
|
|
26
|
+
#: (?checks: String?, ?cannot_check: String?, ?then_run: String?, ?then_run_when: String?) -> void
|
|
27
|
+
def initialize(checks: nil, cannot_check: nil, then_run: nil, then_run_when: nil)
|
|
28
|
+
@checks = checks
|
|
29
|
+
@cannot_check = cannot_check
|
|
30
|
+
@then_run = then_run
|
|
31
|
+
@then_run_when = then_run_when
|
|
32
|
+
freeze
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# The command to run next ends its line, with no backticks, so it pastes as it stands.
|
|
36
|
+
#: (String) -> Array[String]
|
|
37
|
+
def lines(executable)
|
|
38
|
+
[("The tool then checks that #{checks}" if checks), ("It cannot check #{cannot_check}" if cannot_check),
|
|
39
|
+
("#{then_run_when ? "#{then_run_when}, run" : "Then run"}: #{executable} #{then_run}" if then_run)].compact
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# One numbered step as the page prints it: whose step it is, what it does, what follows an agent's
|
|
44
|
+
# step, and each branch's condition with the number of the step it leads to.
|
|
45
|
+
class Step
|
|
46
|
+
#: bool
|
|
47
|
+
attr_reader :agent
|
|
48
|
+
#: String
|
|
49
|
+
attr_reader :does
|
|
50
|
+
#: Handover
|
|
51
|
+
attr_reader :handover
|
|
52
|
+
#: Array[[String, Integer]]
|
|
53
|
+
attr_reader :branches
|
|
54
|
+
|
|
55
|
+
#: (agent: bool, does: String, ?handover: Handover, ?branches: Array[[String, Integer]]) -> void
|
|
56
|
+
def initialize(agent:, does:, handover: Handover.new, branches: [])
|
|
57
|
+
@agent = agent
|
|
58
|
+
@does = does
|
|
59
|
+
@handover = handover
|
|
60
|
+
@branches = branches.freeze #: Array[[String, Integer]]
|
|
61
|
+
freeze
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# The overview: what the tool is, each group's heading with its commands' usage and summary, and how
|
|
66
|
+
# the commands chain, with each command it prints started by the running executable.
|
|
67
|
+
class Overview
|
|
68
|
+
#: (version: String, groups: Array[[String, Array[[String, String]]]], executable: String) -> void
|
|
69
|
+
def initialize(version:, groups:, executable:)
|
|
70
|
+
@version = version
|
|
71
|
+
@groups = groups
|
|
72
|
+
@executable = executable
|
|
73
|
+
freeze
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
#: () -> String
|
|
77
|
+
def to_s
|
|
78
|
+
commands = @groups.map do |heading, entries|
|
|
79
|
+
"## #{heading}\n\n#{entries.map { |usage, summary| "- `#{usage}`: #{summary}" }.join("\n")}"
|
|
80
|
+
end
|
|
81
|
+
Prompts.markdown("learn-what-a-command-does/every-command-and-how-they-chain",
|
|
82
|
+
version: @version, commands: commands.join("\n\n"), executable: @executable)
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
# One command's page, headed by the command as the running executable takes it: its prose prompt, what it
|
|
87
|
+
# writes, then its numbered steps.
|
|
88
|
+
class CommandPage
|
|
89
|
+
#: (usage: String, prompt: String, writes: String, steps: Array[Step], executable: String) -> void
|
|
90
|
+
def initialize(usage:, prompt:, writes:, steps:, executable:)
|
|
91
|
+
@usage = usage
|
|
92
|
+
@executable = executable
|
|
93
|
+
@prompt = prompt
|
|
94
|
+
@writes = writes
|
|
95
|
+
@steps = steps
|
|
96
|
+
freeze
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
#: () -> String
|
|
100
|
+
def to_s
|
|
101
|
+
numbered = @steps.each_with_index.map { |step, index| "#{index + 1}. #{step_text(step)}" }
|
|
102
|
+
["# #{@executable} #{@usage}", Prompts.markdown(@prompt, executable: @executable), @writes, "## Steps",
|
|
103
|
+
numbered.join("\n"), "A step that fails prints a refusal with its code and a repair.",
|
|
104
|
+
"`--project PATH` is optional: it defaults to the current directory."].join("\n\n")
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
private
|
|
108
|
+
|
|
109
|
+
#: (Step) -> String
|
|
110
|
+
def step_text(step)
|
|
111
|
+
branches = step.branches.map { |condition, number| "If #{condition}: step #{number}." }
|
|
112
|
+
[step.agent ? "**You:** #{step.does}" : "The tool: #{step.does}", *step.handover.lines(@executable),
|
|
113
|
+
*branches]
|
|
114
|
+
.join("\n ")
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
end
|
|
@@ -0,0 +1,66 @@
|
|
|
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
|
+
# How the running tool is invoked, so a printed command names the executable and project that are
|
|
10
|
+
# running now. A controller builds it for each run; a view uses it to print commands, and it is never
|
|
11
|
+
# stored (alpha 008 friction 16).
|
|
12
|
+
class Invocation
|
|
13
|
+
#: String
|
|
14
|
+
attr_reader :executable
|
|
15
|
+
#: String
|
|
16
|
+
attr_reader :project
|
|
17
|
+
|
|
18
|
+
#: (executable: String, project: String) -> void
|
|
19
|
+
def initialize(executable:, project:)
|
|
20
|
+
@executable = executable
|
|
21
|
+
@project = project
|
|
22
|
+
freeze
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# A command to run in this project, which it names right after the executable, as model repairs do (#580).
|
|
26
|
+
# Each value is shell-quoted, because ids, paths and headings come from project files that anyone may have
|
|
27
|
+
# written (#227). options are flags with their values, such as
|
|
28
|
+
# ["--starting-at", text], quoted the same way. usage is this tool's own placeholder text, such as
|
|
29
|
+
# "--section HEADING", left for the agent to fill and never quoted. When a value starts with "-", as a
|
|
30
|
+
# path may, every option comes first and "--" ends them, so the value is not read as an option (#240).
|
|
31
|
+
#: (String, *String, ?options: Array[String], ?usage: String?) -> String
|
|
32
|
+
def in_project(command, *values, options: [], usage: nil) = listed_in_project(command, values, options:, usage:)
|
|
33
|
+
|
|
34
|
+
# The command that answers request request_id in this project: from file, the --file value an answer was
|
|
35
|
+
# checked from, as given, or before there is one, from a FILE the agent names (#859).
|
|
36
|
+
#: (String, ?file: String?) -> String
|
|
37
|
+
def submit_answer(request_id, file: nil)
|
|
38
|
+
return in_project("submit-answer", request_id, usage: "--file FILE") unless file
|
|
39
|
+
|
|
40
|
+
in_project("submit-answer", request_id, options: ["--file", file])
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# The same command, its values given as one list, as a refusal's repair command holds them (D56).
|
|
44
|
+
#: (String, Array[String], ?options: Array[String], ?usage: String?) -> String
|
|
45
|
+
def listed_in_project(command, values, options: [], usage: nil)
|
|
46
|
+
flags = options.map { |value| shell_word(value) }
|
|
47
|
+
words = values.map { |value| shell_word(value) }
|
|
48
|
+
if values.any? { |value| value.start_with?("-") }
|
|
49
|
+
return [@executable, project_option, command, *flags, usage, "--", *words].compact.join(" ")
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
[@executable, project_option, command, *words, *flags, usage].compact.join(" ")
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
#: () -> String
|
|
56
|
+
def project_option
|
|
57
|
+
"--project #{shell_word(@project)}"
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
private
|
|
61
|
+
|
|
62
|
+
#: (String) -> String
|
|
63
|
+
def shell_word(value) = ShellWord.new(value).to_s
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# typed: strict
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
module Principles
|
|
5
|
+
# What this tool prints, from plain values a controller hands it. views/CLAUDE.md owns the boundary.
|
|
6
|
+
module Views
|
|
7
|
+
# In git, what landed after a completed audit's base commit, as its result prints it: the commits since the base
|
|
8
|
+
# that touched each member the audit changed, by member id, and the base and HEAD when HEAD differs from the base
|
|
9
|
+
# and none touched one (#740). Outside git a completed audit has none, so it is given only in git.
|
|
10
|
+
class LandedSinceBase
|
|
11
|
+
#: (commits_by_member: Hash[String, Array[Hash[String, String]]], ?head_past_base: [String, String]?) -> void
|
|
12
|
+
def initialize(commits_by_member:, head_past_base: nil)
|
|
13
|
+
@commits_by_member = commits_by_member
|
|
14
|
+
@head_past_base = head_past_base
|
|
15
|
+
freeze
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# The fields as printed: commits_since_base, and head_moved_past_base only when head_past_base is given.
|
|
19
|
+
#: () -> Hash[String, untyped]
|
|
20
|
+
def to_h
|
|
21
|
+
printed = { "commits_since_base" => @commits_by_member } #: Hash[String, untyped]
|
|
22
|
+
base, head = @head_past_base
|
|
23
|
+
@head_past_base ? printed.merge("head_moved_past_base" => { "base_commit" => base, "head" => head }) : printed
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|