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,131 @@
|
|
|
1
|
+
# typed: strict
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require_relative "../error"
|
|
5
|
+
require_relative "answer_fields"
|
|
6
|
+
require_relative "loads_to_check"
|
|
7
|
+
require_relative "member_assessments"
|
|
8
|
+
require_relative "misspelt_member_id"
|
|
9
|
+
require_relative "record_judged_against"
|
|
10
|
+
|
|
11
|
+
module Principles
|
|
12
|
+
# The domain logic, and the mediation of persistence. models/CLAUDE.md owns the boundary.
|
|
13
|
+
module Models
|
|
14
|
+
# An agent's answer to an assessment request: one assessment per member, an assessment of the set, a
|
|
15
|
+
# recent decision under it, and findings. The block locates each piece of evidence in the project and
|
|
16
|
+
# refuses one that does not resolve. members are the audited members, each checked one's assessment reporting
|
|
17
|
+
# its check's result (D30); judged_against is the principle's record as it stands, which lists the members
|
|
18
|
+
# whose loading it must decide (#355) and the assessments it may carry (D49); answered_at is when it came, which
|
|
19
|
+
# no sighting it reports is after (#699).
|
|
20
|
+
class AssessmentAnswer
|
|
21
|
+
#: (untyped, MapMembers, answered_at: String, ?judged_against: RecordJudgedAgainst?)
|
|
22
|
+
#| { (Locator) -> Hash[String, untyped] } -> void
|
|
23
|
+
def initialize(response, members, answered_at:, judged_against: nil, &locate)
|
|
24
|
+
@response = response
|
|
25
|
+
@members = members
|
|
26
|
+
@answered_at = answered_at
|
|
27
|
+
@judged_against = judged_against
|
|
28
|
+
@loads = judged_against&.loads #: LoadsToCheck?
|
|
29
|
+
@locate = locate #: ^(Locator) -> Hash[String, untyped]
|
|
30
|
+
@fields = AnswerFields.new(locate) #: AnswerFields
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# The answer's findings, once every member assessment, the collective assessment, the recent
|
|
34
|
+
# decision and each finding have been checked.
|
|
35
|
+
#: () -> Array[untyped]
|
|
36
|
+
def findings
|
|
37
|
+
member_assessments.check
|
|
38
|
+
@fields.text(@response["collective_assessment"], "collective_assessment")
|
|
39
|
+
recent_decision
|
|
40
|
+
checked_findings
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# The result reported for each checked member's check, by member id, once each has been checked.
|
|
44
|
+
#: () -> Hash[String, Hash[String, untyped]]
|
|
45
|
+
def check_results = member_assessments.check_results
|
|
46
|
+
|
|
47
|
+
# By member id, the fields this answer reports for each member's record entry: its assessment, which the next
|
|
48
|
+
# full audit may carry forward (D49), its check's result (D30) and its accepted loading decision (#355), once
|
|
49
|
+
# each listed member's is checked against loads.
|
|
50
|
+
#: () -> Hash[String, Hash[String, untyped]]
|
|
51
|
+
def entry_fields
|
|
52
|
+
checked = check_results.transform_values do |result|
|
|
53
|
+
{ "check_result" => result }
|
|
54
|
+
end #: Hash[String, Hash[String, untyped]]
|
|
55
|
+
accepted = (@loads&.decided(@response["loading_decisions"]) || {}).transform_values do |kept|
|
|
56
|
+
{ "loading_accepted" => kept }
|
|
57
|
+
end
|
|
58
|
+
reported = checked.merge(accepted) { |_id, check, loading| check.merge(loading) }
|
|
59
|
+
member_assessments.kept(&@locate).merge(reported) { |_id, assessment, fields| assessment.merge(fields) }
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# The paths the recent decision's evidence cites, which the record does not keep but a completed audit asks to
|
|
63
|
+
# commit when they differ from HEAD (#860); none when the decision was unavailable.
|
|
64
|
+
#: () -> Array[String]
|
|
65
|
+
def recent_decision_paths
|
|
66
|
+
decision = @response.fetch("recent_decision")
|
|
67
|
+
return [] if decision["unavailable_reason"]
|
|
68
|
+
|
|
69
|
+
decision.fetch("evidence").map { |item| item.fetch("path") }.uniq
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
private
|
|
73
|
+
|
|
74
|
+
#: () -> MemberAssessments
|
|
75
|
+
def member_assessments
|
|
76
|
+
MemberAssessments.new(@response["member_assessments"], @members, fields: @fields,
|
|
77
|
+
judged_against: @judged_against,
|
|
78
|
+
answered_at: @answered_at)
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
#: () -> void
|
|
82
|
+
def recent_decision
|
|
83
|
+
decision = @response.fetch("recent_decision")
|
|
84
|
+
if decision["unavailable_reason"]
|
|
85
|
+
@fields.text(decision["unavailable_reason"], "recent_decision.unavailable_reason")
|
|
86
|
+
else
|
|
87
|
+
@fields.text(decision["assessment"], "recent_decision.assessment")
|
|
88
|
+
@fields.evidence(decision["evidence"], AnswerPlace.new("/recent_decision/evidence", "Recent decision"))
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
#: () -> Array[untyped]
|
|
93
|
+
def checked_findings
|
|
94
|
+
findings = @fields.array(@response["findings"], "findings")
|
|
95
|
+
finding_ids = findings.each_with_index.map { |finding, index| finding_id(finding, index) }
|
|
96
|
+
unless finding_ids.uniq == finding_ids
|
|
97
|
+
raise Error.new("duplicate_finding", "Finding ids repeat",
|
|
98
|
+
"Give each finding a unique id.")
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
findings
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
# The id of finding, once its fields, members and evidence have been checked.
|
|
105
|
+
#: (untyped, Integer) -> untyped
|
|
106
|
+
def finding_id(finding, index)
|
|
107
|
+
@fields.text(finding["id"], "finding.id")
|
|
108
|
+
%w[problem consequence proposed_action].each { |field| @fields.text(finding[field], "finding.#{field}") }
|
|
109
|
+
affected_members(finding)
|
|
110
|
+
place = AnswerPlace.new("/findings/#{index}", "Finding #{finding["id"]}")
|
|
111
|
+
@fields.evidence(finding["evidence"], place.child("evidence"))
|
|
112
|
+
finding.fetch("id")
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
# Refuses a finding that names no member, or one the map does not have.
|
|
116
|
+
#: (untyped) -> void
|
|
117
|
+
def affected_members(finding)
|
|
118
|
+
affected = @fields.array(finding["member_ids"], "finding.member_ids")
|
|
119
|
+
ids = @members.ids
|
|
120
|
+
return unless affected.empty? || (affected - ids).any?
|
|
121
|
+
|
|
122
|
+
raise Error.new("unknown_member", "Finding #{finding["id"]} names unknown or no members",
|
|
123
|
+
[*near_misses(affected - ids), "Use one or more of #{ids.join(", ")}."].join(" "))
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
# A hint for each unmapped id that differs from a mapped one only in punctuation, case or spacing.
|
|
127
|
+
#: (Array[untyped]) -> Array[String]
|
|
128
|
+
def near_misses(unknown) = unknown.grep(String).filter_map { |id| MisspeltMemberId.new(id).hint(@members.ids) }
|
|
129
|
+
end
|
|
130
|
+
end
|
|
131
|
+
end
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# typed: strict
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require_relative "audit_record"
|
|
5
|
+
require_relative "last_completed_revision"
|
|
6
|
+
|
|
7
|
+
module Principles
|
|
8
|
+
# The domain logic, which loads and saves itself through data/. models/CLAUDE.md owns the boundary.
|
|
9
|
+
module Models
|
|
10
|
+
# What an assessment request prints as its basis, which the agent hands back with the answer: the commit the
|
|
11
|
+
# audit is based on, nil outside git, and the revision of the last completed audit record when the request was
|
|
12
|
+
# printed, nil when there was none, so the answer is refused once that record changed (#653, D50).
|
|
13
|
+
class AssessmentBasis
|
|
14
|
+
# The basis printed for an audit based on base, after record, the principle's audit record now, if any.
|
|
15
|
+
#: (String?, AuditRecord?) -> AssessmentBasis
|
|
16
|
+
def self.printed(base, record)
|
|
17
|
+
new("base_commit" => base, "last_completed_revision" => record&.last_completed&.revision)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
#: (Hash[String, untyped]) -> void
|
|
21
|
+
def initialize(fields)
|
|
22
|
+
@fields = fields
|
|
23
|
+
freeze
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
#: () -> Hash[String, untyped]
|
|
27
|
+
def to_h = @fields
|
|
28
|
+
|
|
29
|
+
# The commit the audit is based on, nil outside git.
|
|
30
|
+
#: () -> String?
|
|
31
|
+
def base = @fields["base_commit"]
|
|
32
|
+
|
|
33
|
+
# The last completed record's revision when the request was printed, nil when there was none, which the
|
|
34
|
+
# correction the assessment prints carries forward (D50).
|
|
35
|
+
#: () -> String?
|
|
36
|
+
def revision = @fields["last_completed_revision"]
|
|
37
|
+
|
|
38
|
+
# The last completed record's revision when the request was printed, which refuses the answer once it changed.
|
|
39
|
+
#: () -> LastCompletedRevision
|
|
40
|
+
def printed_after = LastCompletedRevision.new(revision)
|
|
41
|
+
|
|
42
|
+
# What the request id digests of the basis, in a fixed order, so how the answer orders its keys changes nothing.
|
|
43
|
+
#: () -> Array[String?]
|
|
44
|
+
def digested = [base, revision]
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# typed: strict
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require_relative "assessment_answer"
|
|
5
|
+
require_relative "member_grounds"
|
|
6
|
+
require_relative "member_moves"
|
|
7
|
+
|
|
8
|
+
module Principles
|
|
9
|
+
# The domain logic, and the mediation of persistence. models/CLAUDE.md owns the boundary.
|
|
10
|
+
module Models
|
|
11
|
+
# What an assessment reported beyond its findings, which its audit's record and commit carry: by member id, the
|
|
12
|
+
# fields of each member's record entry, such as its assessment (D49) and its check's result (D30), and the paths
|
|
13
|
+
# its recent decision cites, which the record does not keep but the completed audit asks to commit when they
|
|
14
|
+
# differ from HEAD (#860). A correction request carries it in its basis, so the completed audit keeps it; this is
|
|
15
|
+
# the one owner of how it is carried.
|
|
16
|
+
class AssessmentReport
|
|
17
|
+
# The report of the answer assessed, each assessment it makes, rather than carries, with the grounds it was
|
|
18
|
+
# judged on (D49).
|
|
19
|
+
#: (AssessmentAnswer, MemberGrounds) -> AssessmentReport
|
|
20
|
+
def self.answered(assessed, grounds)
|
|
21
|
+
fields = assessed.entry_fields.to_h do |id, entry|
|
|
22
|
+
kept = entry["assessment"]
|
|
23
|
+
[id, kept && !kept["carried_forward"] ? entry.merge("assessment" => kept.merge(grounds.of(id))) : entry]
|
|
24
|
+
end
|
|
25
|
+
new(fields, recent_decision_paths: assessed.recent_decision_paths)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# The report carried in a correction basis, under the ids of any members moves moved.
|
|
29
|
+
#: (CorrectionBasis, MemberMoves) -> AssessmentReport
|
|
30
|
+
def self.carried(basis, moves)
|
|
31
|
+
fields = basis.to_h
|
|
32
|
+
new(moves.hashes(fields.fetch("entry_fields", {})),
|
|
33
|
+
recent_decision_paths: fields.fetch("recent_decision_paths", []))
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# The paths the recent decision cites as evidence.
|
|
37
|
+
#: Array[String]
|
|
38
|
+
attr_reader :recent_decision_paths
|
|
39
|
+
|
|
40
|
+
# entry_fields are, by member id, the fields the assessment reported for that member's record entry.
|
|
41
|
+
#: (?Hash[String, Hash[String, untyped]], ?recent_decision_paths: Array[String]) -> void
|
|
42
|
+
def initialize(entry_fields = {}, recent_decision_paths: [])
|
|
43
|
+
@entry_fields = entry_fields #: Hash[String, Hash[String, untyped]]
|
|
44
|
+
@recent_decision_paths = recent_decision_paths
|
|
45
|
+
freeze
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# The fields the assessment reported for the record entry of the member known by id, or none.
|
|
49
|
+
#: (String) -> Hash[String, untyped]
|
|
50
|
+
def entry_fields_of(id) = @entry_fields.fetch(id, {})
|
|
51
|
+
|
|
52
|
+
# The fields a correction basis carries it in, leaving out what it does not hold.
|
|
53
|
+
#: () -> Hash[String, untyped]
|
|
54
|
+
def basis_fields
|
|
55
|
+
{ "entry_fields" => @entry_fields, "recent_decision_paths" => @recent_decision_paths }
|
|
56
|
+
.reject { |_key, value| value.empty? }
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
NONE = new #: AssessmentReport
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
# typed: strict
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require_relative "../error"
|
|
5
|
+
require_relative "member_map"
|
|
6
|
+
require_relative "declared_map_changes"
|
|
7
|
+
require_relative "request_id"
|
|
8
|
+
require_relative "correction_basis"
|
|
9
|
+
require_relative "assessment_basis"
|
|
10
|
+
require_relative "assessment_answer"
|
|
11
|
+
require_relative "audit_start"
|
|
12
|
+
require_relative "assessment_report"
|
|
13
|
+
require_relative "loads_to_check"
|
|
14
|
+
require_relative "member_grounds"
|
|
15
|
+
require_relative "carried_assessments"
|
|
16
|
+
require_relative "prior_findings"
|
|
17
|
+
require_relative "decided_prior_findings"
|
|
18
|
+
require_relative "disposition"
|
|
19
|
+
|
|
20
|
+
module Principles
|
|
21
|
+
# The domain logic, and the mediation of persistence. models/CLAUDE.md owns the boundary.
|
|
22
|
+
module Models
|
|
23
|
+
# One principle's audit as its map and members are now. Nothing about a request is stored (D25): each
|
|
24
|
+
# request id digests what the request depends on, so an answer is refused when what it was asked about
|
|
25
|
+
# has changed since. A correction is based on the map's version and each finding's members, never on
|
|
26
|
+
# the agent's prose.
|
|
27
|
+
class Audit
|
|
28
|
+
#: MemberMap
|
|
29
|
+
attr_reader :map
|
|
30
|
+
# Each member's hash now.
|
|
31
|
+
#: Hash[String, String]
|
|
32
|
+
attr_reader :now
|
|
33
|
+
|
|
34
|
+
#: (map: MemberMap, now: Hash[String, String]) -> void
|
|
35
|
+
def initialize(map:, now:)
|
|
36
|
+
@map = map
|
|
37
|
+
@now = now
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# The principle audited, as its map names it.
|
|
41
|
+
#: () -> String
|
|
42
|
+
def id = @map.id
|
|
43
|
+
|
|
44
|
+
#: () -> String
|
|
45
|
+
def map_version = @map.version
|
|
46
|
+
|
|
47
|
+
# The id of the assessment request printed with basis: its base commit, nil outside git, and the last
|
|
48
|
+
# completed record's revision, so an answer cannot point the request at a later record (#653).
|
|
49
|
+
# prior is what PriorFindings#digested gives, empty when the last audit left no finding to decide (D35);
|
|
50
|
+
# loads is what LoadsToCheck#listed gives, nil when the activation lists nothing to check (#355).
|
|
51
|
+
#: (AssessmentBasis, ?Array[Hash[String, untyped]], ?Hash[String, untyped]?) -> RequestId
|
|
52
|
+
def assessment_id(basis, prior = [], loads = nil)
|
|
53
|
+
parts = [map_version, basis.digested, @now]
|
|
54
|
+
parts = [*parts, prior] unless prior.empty?
|
|
55
|
+
parts = [*parts, { "loads_to_check" => loads }] if loads
|
|
56
|
+
RequestId.for(principle_id: id, phase: "assessment", parts: parts)
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# Refuses an assessment unless request_id is the one printed with basis for the map and members as they are
|
|
60
|
+
# now, and, in git, the members are as its base holds them (committed).
|
|
61
|
+
# It returns the findings the last audit, whose record is given, left for this one to decide (D35); loads is
|
|
62
|
+
# what the audit lists to check now, so a change to what lists a member to check makes the request stale (#355).
|
|
63
|
+
#: (RequestId, basis: AssessmentBasis, committed: Hash[String, String]?, record: AuditRecord?,
|
|
64
|
+
#| loads: LoadsToCheck) -> PriorFindings
|
|
65
|
+
def check_assessment_request(request_id, basis:, committed:, record:, loads:)
|
|
66
|
+
prior = prior_findings(record)
|
|
67
|
+
now_id = assessment_id(basis, prior.digested, loads.listed)
|
|
68
|
+
raise stale_request unless now_id == request_id && (basis.base.nil? || committed == @now)
|
|
69
|
+
|
|
70
|
+
prior
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
# The answer to this audit's assessment request, checked against each member, each member's check (D30)
|
|
74
|
+
# and each member the listing names to check against the activation (#355); the block locates each piece of
|
|
75
|
+
# evidence, giving where it is and the hash of its text.
|
|
76
|
+
# judged_against is the principle's record as it stands, which says which members' last assessments the
|
|
77
|
+
# answer may carry forward (D49); answered_at is when the answer came, which no sighting it reports is after
|
|
78
|
+
# (#699).
|
|
79
|
+
#: (untyped, RecordJudgedAgainst, String) { (Locator) -> Hash[String, untyped] }
|
|
80
|
+
#| -> AssessmentAnswer
|
|
81
|
+
def assessment(response, judged_against, answered_at, &)
|
|
82
|
+
AssessmentAnswer.new(response, map_members, answered_at: answered_at, judged_against: judged_against, &)
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
# A digest of what this audit's assessments answer: its description and question (D49).
|
|
86
|
+
#: (String) -> String
|
|
87
|
+
def judged_against(question) = started(nil, {}).judged_against(question)
|
|
88
|
+
|
|
89
|
+
# What this audit began from: base (nil outside git), each member's hash then and what the assessment reported
|
|
90
|
+
# beyond its findings.
|
|
91
|
+
#: (String?, Hash[String, String], ?AssessmentReport) -> AuditStart
|
|
92
|
+
def started(base, before, reported = AssessmentReport::NONE)
|
|
93
|
+
AuditStart.new(map: @map, base: base, before: before, reported: reported)
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
# What an audit answered by assessed began from: base, each member's hash now, and what the answer reported
|
|
97
|
+
# beyond its findings, each assessment with the grounds it was judged on (D49).
|
|
98
|
+
#: (String?, AssessmentAnswer, MemberGrounds) -> AuditStart
|
|
99
|
+
def assessed_start(base, assessed, grounds)
|
|
100
|
+
started(base, @now, AssessmentReport.answered(assessed, grounds))
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
# What the audit a correction's basis names began from: its base commit, each member's hash then
|
|
104
|
+
# (before), and the assessment's report the basis carries, under the ids of any moved members.
|
|
105
|
+
#: (CorrectionBasis, Hash[String, String], MemberMoves) -> AuditStart
|
|
106
|
+
def resumed(basis, before, moves)
|
|
107
|
+
started(basis.base_commit, before, AssessmentReport.carried(basis, moves))
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
# The map the audit began from: the current one when it is unchanged and the answer declares no change; or,
|
|
111
|
+
# in git, the map at the base commit, which the block reads, refused unless the declared changes and any
|
|
112
|
+
# members added explain every change. Outside git, a map that changed is refused.
|
|
113
|
+
#: (CorrectionBasis, DeclaredMapChanges, map_file: String) { (String) -> MemberMap } -> MemberMap
|
|
114
|
+
def map_started_from(basis, changes, map_file:, &)
|
|
115
|
+
base = changes.base_to_read(basis.base_commit, @map, map_version: basis.map_version, map_file: map_file)
|
|
116
|
+
return @map unless base
|
|
117
|
+
|
|
118
|
+
started = yield(base)
|
|
119
|
+
changes.check(started, @map, map_version: basis.map_version, map_file: map_file)
|
|
120
|
+
started
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
# The hashes the audit began from (before), refused unless they were found and request_id is the one
|
|
124
|
+
# printed for them and basis.
|
|
125
|
+
#: (RequestId, CorrectionBasis, Hash[String, String]?) -> Hash[String, String]
|
|
126
|
+
def confirmed_start(request_id, basis, before)
|
|
127
|
+
raise stale_request unless before && basis.request_id(id, before) == request_id
|
|
128
|
+
|
|
129
|
+
before
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
# Each finding's outcome: its members and its disposition's action, where it is recorded, if anywhere, and
|
|
133
|
+
# what a deferral says it waits on, if anything (D52).
|
|
134
|
+
# Then each prior finding kept (D35); a new finding that raises one again keeps when it was first raised.
|
|
135
|
+
#: (Array[untyped], Array[Disposition], ?DecidedPriorFindings) -> Array[untyped]
|
|
136
|
+
def outcomes(findings, dispositions, prior = DecidedPriorFindings::NONE)
|
|
137
|
+
findings.map do |finding|
|
|
138
|
+
id = finding.fetch("id")
|
|
139
|
+
disposition = dispositions.find { |each| each.finding_id == id } or raise KeyError, id
|
|
140
|
+
finding.slice(*RecordedFinding::RAISED_FIELDS).merge(disposition.recorded_outcome, prior.first_raised(id))
|
|
141
|
+
end + prior.kept
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
# What each member's assessment is judged on beside its text, from each member's loads: its map entry and when
|
|
145
|
+
# it loads (D49).
|
|
146
|
+
#: (Hash[String, MemberLoads]) -> MemberGrounds
|
|
147
|
+
def grounds(loads) = MemberGrounds.new(map_members, loads)
|
|
148
|
+
|
|
149
|
+
# Which members' last assessments this audit carries forward, from the principle's record now, grounds, and the
|
|
150
|
+
# question the request asks (D49).
|
|
151
|
+
#: (AuditRecord?, grounds: MemberGrounds, question: String) -> CarriedAssessments
|
|
152
|
+
def carried_assessments(record, grounds:, question:)
|
|
153
|
+
CarriedAssessments.new(record, members: map_members, judged_against: judged_against(question), now: @now,
|
|
154
|
+
grounds: grounds)
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
# The members this audit lists to check against the map's activation, from each member's loads (#355), each
|
|
158
|
+
# with the decision the last completed audit, whose record is given, accepted on its loading (#502).
|
|
159
|
+
#: (Hash[String, MemberLoads], ?AuditRecord?) -> LoadsToCheck
|
|
160
|
+
def loads_to_check(loads, record = nil)
|
|
161
|
+
completed = record&.last_completed
|
|
162
|
+
LoadsToCheck.new(activation: @map.activation, loads: loads, hashes: @now,
|
|
163
|
+
accepted: completed ? completed.members.loading_accepted(map_members) : {})
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
# The findings the last audit left for this one to decide (D35), from its record.
|
|
167
|
+
#: (AuditRecord?) -> PriorFindings
|
|
168
|
+
def prior_findings(record)
|
|
169
|
+
PriorFindings.new(record: record, now: @now, members: map_members)
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
# The project files the dispositions say changed: each fixed member's file, then each file where a
|
|
173
|
+
# finding is recorded.
|
|
174
|
+
#: (Array[Disposition]) -> Array[String]
|
|
175
|
+
def changed_files(dispositions)
|
|
176
|
+
fixed = dispositions.select(&:fixed?).flat_map(&:changed_members).map { |member| path_of(member) }
|
|
177
|
+
(fixed + dispositions.filter_map(&:recorded_path)).uniq
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
# The path of the member with member_id in the map, refused as unknown_member when no member has it (#778).
|
|
181
|
+
#: (String) -> String
|
|
182
|
+
def path_of(member_id) = map_members.found(member_id).path
|
|
183
|
+
|
|
184
|
+
private
|
|
185
|
+
|
|
186
|
+
# The members as the map lists them, which say each one's id and check.
|
|
187
|
+
#: () -> MapMembers
|
|
188
|
+
def map_members = @map.members
|
|
189
|
+
|
|
190
|
+
#: () -> Error
|
|
191
|
+
def stale_request
|
|
192
|
+
Error.new("stale_request", "This request no longer matches #{id}: its map, a member, the commit it " \
|
|
193
|
+
"was based on, or what says when a listed member loads and how big it is " \
|
|
194
|
+
"(project settings, frontmatter or a listed file), has changed since it was " \
|
|
195
|
+
"printed",
|
|
196
|
+
"Start the audit again and answer the new request: #{Error::EXECUTABLE} start-audit #{id}")
|
|
197
|
+
end
|
|
198
|
+
end
|
|
199
|
+
end
|
|
200
|
+
end
|