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,185 @@
|
|
|
1
|
+
# typed: strict
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require_relative "../error"
|
|
5
|
+
require_relative "../data/map_files"
|
|
6
|
+
require_relative "member_map"
|
|
7
|
+
require_relative "meta_principle"
|
|
8
|
+
|
|
9
|
+
module Principles
|
|
10
|
+
# The domain logic, and the mediation of persistence. models/CLAUDE.md owns the boundary.
|
|
11
|
+
module Models
|
|
12
|
+
# Every member map in the project, loaded and saved through Data::MapFiles.
|
|
13
|
+
class MemberMaps
|
|
14
|
+
# A principle id names its principle in lower-kebab-case.
|
|
15
|
+
ID_FORMAT = Data::PrincipleId::FORMAT #: Regexp
|
|
16
|
+
|
|
17
|
+
#: (Data::MapFiles) -> void
|
|
18
|
+
def initialize(files)
|
|
19
|
+
@files = files
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
#: () -> Array[String]
|
|
23
|
+
def ids
|
|
24
|
+
@files.ids
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# The id of map id, or of every map when id is nil; an id no map has is refused, as find refuses it.
|
|
28
|
+
#: (String?) -> Array[String]
|
|
29
|
+
def ids_of(id)
|
|
30
|
+
return ids unless id
|
|
31
|
+
|
|
32
|
+
find(id)
|
|
33
|
+
[id]
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# Every path a map this tool can read names, once each; a map it cannot read names none, so one broken map
|
|
37
|
+
# still leaves the others' files known.
|
|
38
|
+
#: () -> Array[String]
|
|
39
|
+
def readable_paths
|
|
40
|
+
ids.flat_map do |id|
|
|
41
|
+
find(id).members.paths
|
|
42
|
+
rescue Error
|
|
43
|
+
[]
|
|
44
|
+
end.uniq
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# Whether a map named id exists, whatever it holds.
|
|
48
|
+
#: (String) -> bool
|
|
49
|
+
def mapped?(id) = @files.exist?(id)
|
|
50
|
+
|
|
51
|
+
# Whether principles is set up: the meta-principle's map names a keeping place, a member of any kind. That says
|
|
52
|
+
# a place is named, not that the principle is held, which its audit finds. A map this version cannot read, in a
|
|
53
|
+
# newer format, unparseable or invalid, counts, since it may well have members, so no read refuses over it (D75).
|
|
54
|
+
#: () -> bool
|
|
55
|
+
def set_up?
|
|
56
|
+
return false unless mapped?(MetaPrinciple::ID)
|
|
57
|
+
|
|
58
|
+
begin
|
|
59
|
+
!find(MetaPrinciple::ID).unstarted?
|
|
60
|
+
rescue Error
|
|
61
|
+
true
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# The map named id, refused when there is none; Data::MapFiles refuses one that is unparseable.
|
|
66
|
+
#: (String) -> MemberMap
|
|
67
|
+
def find(id)
|
|
68
|
+
refuse_unknown(id)
|
|
69
|
+
unique(MemberMap.new(id, @files.read(id)))
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
# The map id as text read elsewhere holds it, such as its file at commit, the one an audit began from.
|
|
73
|
+
#: (String, String, ?commit: String?) -> MemberMap
|
|
74
|
+
def parsed(id, text, commit: nil)
|
|
75
|
+
unique(MemberMap.new(id, @files.parse(id, text, commit: commit)), commit)
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
# A new map for id with no members, not yet saved. It is refused unless id is lower-kebab-case and not
|
|
79
|
+
# yet mapped, and is not the meta-principle's, which setup starts; only then is the description asked for.
|
|
80
|
+
#: (String) { () -> String } -> MemberMap
|
|
81
|
+
def create(id, &)
|
|
82
|
+
refuse_unavailable(id)
|
|
83
|
+
refuse_meta_principle_id(id)
|
|
84
|
+
MemberMap.new(id, { "description" => yield, "members" => [] })
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
# The map a new member of principle id joins: the map named id, or, while the meta-principle has none, the map
|
|
88
|
+
# it starts with, not yet saved, so that map is written only with its first member (T74). Any other id no map
|
|
89
|
+
# has is refused, as find refuses it.
|
|
90
|
+
#: (String) -> MemberMap
|
|
91
|
+
def addable(id)
|
|
92
|
+
return find(id) if mapped?(id) || !started_by_setup?(id)
|
|
93
|
+
|
|
94
|
+
MemberMap.new(id, MetaPrinciple.starting_fields)
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
# The map named id, refused when it has no members to audit.
|
|
98
|
+
#: (String) -> MemberMap
|
|
99
|
+
def auditable(id)
|
|
100
|
+
map = find(id)
|
|
101
|
+
return map unless map.members.empty?
|
|
102
|
+
|
|
103
|
+
raise Error.new("map_has_no_members", "#{id} has no members to audit",
|
|
104
|
+
"Add its members: #{Error::EXECUTABLE} add-member #{id} PATH --role ROLE", file(id))
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
# Writes map's file and names it, relative to the project.
|
|
108
|
+
#: (MemberMap) -> String
|
|
109
|
+
def save(map)
|
|
110
|
+
@files.write(map.id, map.to_h)
|
|
111
|
+
file(map.id)
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
# The file map id is kept in, relative to the project.
|
|
115
|
+
#: (String) -> String
|
|
116
|
+
def file(id)
|
|
117
|
+
@files.relative(id)
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
# The file map id was kept in under the folder's earlier name, relative to the project (#536).
|
|
121
|
+
#: (String) -> String
|
|
122
|
+
def earlier_file(id)
|
|
123
|
+
@files.earlier_relative(id)
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
private
|
|
127
|
+
|
|
128
|
+
# Whether principle id's map is started by setup and written by the add-member it prints, never by
|
|
129
|
+
# create-principle: only the meta-principle's, whose description and activation ship with the tool (T74).
|
|
130
|
+
#: (String) -> bool
|
|
131
|
+
def started_by_setup?(id) = id == MetaPrinciple::ID
|
|
132
|
+
|
|
133
|
+
# map, refused when two of its members share an id or a key, as MapMembers#refuse_repeats says.
|
|
134
|
+
#: (MemberMap, ?String?) -> MemberMap
|
|
135
|
+
def unique(map, commit = nil)
|
|
136
|
+
where = commit ? "#{file(map.id)} at commit #{commit[0, 12]}" : file(map.id)
|
|
137
|
+
map.members.refuse_repeats(where: where, file: file(map.id))
|
|
138
|
+
map
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
# A principle no map names, with the commands that create one and list the rest. For the meta-principle's id the
|
|
142
|
+
# repair leads to setup, not create-principle, which would give its map another description and no activation;
|
|
143
|
+
# setup writes no map, and the add-member it prints creates it (T74).
|
|
144
|
+
#: (String) -> void
|
|
145
|
+
def refuse_unknown(id)
|
|
146
|
+
return if mapped?(id)
|
|
147
|
+
|
|
148
|
+
raise Error.new("unknown_principle", "No map named #{id}",
|
|
149
|
+
"#{creating(id)}\nTo see the mapped ids: #{Error::EXECUTABLE} list-principles")
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
# The repair line that creates principle id's map.
|
|
153
|
+
#: (String) -> String
|
|
154
|
+
def creating(id)
|
|
155
|
+
return MetaPrinciple::SET_UP if started_by_setup?(id)
|
|
156
|
+
|
|
157
|
+
"To create it: #{Error::EXECUTABLE} create-principle #{id} --description TEXT"
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
# The meta-principle's map is written by the add-member setup prints, with its first member; create-principle
|
|
161
|
+
# would write it with no members, another description and no activation (T74).
|
|
162
|
+
#: (String) -> void
|
|
163
|
+
def refuse_meta_principle_id(id)
|
|
164
|
+
return unless started_by_setup?(id)
|
|
165
|
+
|
|
166
|
+
raise Error.new("meta_principle_id",
|
|
167
|
+
"#{id} is set up by show-meta-principle-passage, whose printed add-member creates its map " \
|
|
168
|
+
"with its first member", MetaPrinciple::SET_UP)
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
#: (String) -> void
|
|
172
|
+
def refuse_unavailable(id)
|
|
173
|
+
unless id.match?(ID_FORMAT)
|
|
174
|
+
raise Error.new("invalid_id", "#{id} is not lower-kebab-case",
|
|
175
|
+
"Name the principle in lower-kebab-case, such as express-intent.")
|
|
176
|
+
end
|
|
177
|
+
return unless @files.exist?(id)
|
|
178
|
+
|
|
179
|
+
raise Error.new("principle_exists", "#{id} is already mapped",
|
|
180
|
+
"To change its description: #{Error::EXECUTABLE} change-description #{id} --description TEXT",
|
|
181
|
+
file(id))
|
|
182
|
+
end
|
|
183
|
+
end
|
|
184
|
+
end
|
|
185
|
+
end
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
# typed: strict
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require_relative "../error"
|
|
5
|
+
require_relative "member"
|
|
6
|
+
require_relative "member_map"
|
|
7
|
+
|
|
8
|
+
module Principles
|
|
9
|
+
# The domain: maps, audits and command steps, with every rule a schema cannot express.
|
|
10
|
+
# models/CLAUDE.md owns the boundary.
|
|
11
|
+
module Models
|
|
12
|
+
# The members a correction says the agent moved after the audit began, from each old id to its new one. A
|
|
13
|
+
# fix can rewrite the text a member's locator quotes; the agent then runs move-member and declares the
|
|
14
|
+
# move, and this tool checks it against the map at the base commit (D28).
|
|
15
|
+
class MemberMoves
|
|
16
|
+
#: (untyped) -> void
|
|
17
|
+
def initialize(moves)
|
|
18
|
+
@moves = parsed(moves) #: Hash[String, String]
|
|
19
|
+
freeze
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
#: () -> bool
|
|
23
|
+
def empty?
|
|
24
|
+
@moves.empty?
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# Refuses moves outside git, where there is no base map to check them against.
|
|
28
|
+
#: (String?, principle_id: String) -> String
|
|
29
|
+
def check_in_git(base, principle_id:)
|
|
30
|
+
return base if base
|
|
31
|
+
|
|
32
|
+
raise Error.new("moves_need_git", "moved_members needs git, to read the map #{principle_id} began from",
|
|
33
|
+
"Outside git, start a fresh audit: #{Error::EXECUTABLE} start-audit #{principle_id}")
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# The base map with each declared member moved to its place in current, each keeping its role.
|
|
37
|
+
#: (MemberMap, MemberMap) -> MemberMap
|
|
38
|
+
def applied(base, current)
|
|
39
|
+
@moves.reduce(base) { |map, (from, to)| map.moving(from, located(current, to)) }
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# The refusal of a map that changed beyond the moves declared.
|
|
43
|
+
#: (String, String) -> Error
|
|
44
|
+
def undeclared(principle_id, map_file)
|
|
45
|
+
Error.new("undeclared_move", "The map of #{principle_id} changed during this audit beyond the moves declared",
|
|
46
|
+
"Declare each member you moved in moved_members, or start a fresh audit: " \
|
|
47
|
+
"#{Error::EXECUTABLE} start-audit #{principle_id}", map_file)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# The member's id after the declared moves.
|
|
51
|
+
#: (String) -> String
|
|
52
|
+
def renamed(member_id)
|
|
53
|
+
@moves.fetch(member_id, member_id)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# Each finding with its members' ids after the moves.
|
|
57
|
+
#: (Array[untyped]) -> Array[untyped]
|
|
58
|
+
def findings(findings)
|
|
59
|
+
findings.map { |finding| finding.merge("member_ids" => finding.fetch("member_ids").map { |id| renamed(id) }) }
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# The values in by_id, such as each member's hash or its record entry's fields, keyed by each member's id
|
|
63
|
+
# after the moves.
|
|
64
|
+
#: [V] (Hash[String, V]) -> Hash[String, V]
|
|
65
|
+
def hashes(by_id)
|
|
66
|
+
by_id.transform_keys { |id| renamed(id) }
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
private
|
|
70
|
+
|
|
71
|
+
#: (untyped) -> Hash[String, String]
|
|
72
|
+
def parsed(moves)
|
|
73
|
+
return {} if moves.nil?
|
|
74
|
+
|
|
75
|
+
unless moves.is_a?(Array) && moves.all? { |move| move.is_a?(Hash) && move.values_at("from", "to").all?(String) }
|
|
76
|
+
raise Error.new("invalid_answer", "moved_members must be a list of {\"from\": OLD_ID, \"to\": NEW_ID}",
|
|
77
|
+
"List each member you moved with move-member, by its id before and after.")
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
moves.to_h { |move| [move.fetch("from"), move.fetch("to")] }
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
# The current member to's place in its file, without its role.
|
|
84
|
+
#: (MemberMap, String) -> Hash[String, untyped]
|
|
85
|
+
def located(current, to)
|
|
86
|
+
unless current.members.taken?(to)
|
|
87
|
+
raise Error.new("unknown_member", "#{to} is not a member of #{current.id}",
|
|
88
|
+
"Name in moved_members the id show-principle lists after the move.")
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
current.members.found(to).to_h.except("role")
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
end
|
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
# typed: strict
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require_relative "../error"
|
|
5
|
+
require_relative "audit_record"
|
|
6
|
+
require_relative "map_members"
|
|
7
|
+
require_relative "member_map_history"
|
|
8
|
+
require_relative "member_state"
|
|
9
|
+
require_relative "locate_refusal"
|
|
10
|
+
|
|
11
|
+
module Principles
|
|
12
|
+
# The domain logic, which loads and saves itself through data/. models/CLAUDE.md owns the boundary.
|
|
13
|
+
module Models
|
|
14
|
+
# One map's members, each as broken, changed, unchanged, unchanged since its mapping commit or with no
|
|
15
|
+
# baseline, against what this tool last knew of its text: the last audit's hash for it, or else its text at
|
|
16
|
+
# the commit that mapped it where it is.
|
|
17
|
+
# A member is changed only when its own text changed, so an edit elsewhere in its file is not one.
|
|
18
|
+
class MemberProblems
|
|
19
|
+
# What this tool last knew of each member's text, by key: the last audit's hashes and the commit it began
|
|
20
|
+
# from, and each member's hash and commit at the commit that mapped it where it is (#243).
|
|
21
|
+
class Baselines
|
|
22
|
+
# The last audit's hashes for members' map, and each member's hash and commit at the commit that mapped it
|
|
23
|
+
# where it is, read with the map as that commit held it; none outside git.
|
|
24
|
+
#: (Project, MapMembers) -> Baselines
|
|
25
|
+
def self.of(project, members)
|
|
26
|
+
id = members.principle_id
|
|
27
|
+
audit = project.audit_records.find(id)
|
|
28
|
+
mapped = MemberMapHistory.new(id: id, history: project.history, member_maps: project.member_maps)
|
|
29
|
+
.mapped_baselines(members.keys)
|
|
30
|
+
new(audit: audit, mapped: mapped.transform_values { |each| each.fetch("sha256") },
|
|
31
|
+
mapped_in: mapped.transform_values { |each| each.fetch("commit") }, in_git: project.history.inside_git?)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# Whether the project is in git, so a member with no baseline is one no commit holds yet.
|
|
35
|
+
#: bool
|
|
36
|
+
attr_reader :in_git
|
|
37
|
+
|
|
38
|
+
# audit is the principle's audit record, if any: its hashes, the commit it began from and whether it is
|
|
39
|
+
# completed, so a one-member audit is offered only where one would be accepted (#626).
|
|
40
|
+
#: (audit: AuditRecord?, mapped: Hash[String, String], mapped_in: Hash[String, String], in_git: bool)
|
|
41
|
+
#| -> void
|
|
42
|
+
def initialize(audit:, mapped:, mapped_in:, in_git:)
|
|
43
|
+
@audited = audit&.members&.hashes_by_key || {} #: Hash[String, String]
|
|
44
|
+
@audit = audit #: AuditRecord?
|
|
45
|
+
@audit_base = audit&.base_commit #: String?
|
|
46
|
+
@mapped = mapped
|
|
47
|
+
@mapped_in = mapped_in
|
|
48
|
+
@in_git = in_git
|
|
49
|
+
freeze
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# The hash this tool last knew for the member known by key, and what it was: the last audit's, or else its
|
|
53
|
+
# hash at the commit that mapped it, with why no audit gave one; neither when it has no baseline.
|
|
54
|
+
#: (String) -> [String?, Hash[String, untyped]?]
|
|
55
|
+
def of(key)
|
|
56
|
+
if @audited.key?(key)
|
|
57
|
+
[@audited[key], { "baseline" => "audit", "commit" => @audit_base }]
|
|
58
|
+
elsif @mapped.key?(key)
|
|
59
|
+
[@mapped[key], { "baseline" => "mapping_commit", "commit" => @mapped_in[key],
|
|
60
|
+
"no_audit_because" => @audited.empty? ? "no_audit_record" : "not_in_last_audit" }]
|
|
61
|
+
else
|
|
62
|
+
[nil, nil]
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
# Why a member these baselines do not hold has none: in git no commit holds it where its map places
|
|
67
|
+
# it, and outside git there is no history.
|
|
68
|
+
#: () -> String
|
|
69
|
+
def none_because = @in_git ? "not_committed" : "outside_git"
|
|
70
|
+
|
|
71
|
+
# Whether no full audit of the principle has completed: it has no record, or an open one that keeps none
|
|
72
|
+
# (D58).
|
|
73
|
+
#: () -> bool
|
|
74
|
+
def no_completed_audit?
|
|
75
|
+
audit = @audit
|
|
76
|
+
audit.nil? || audit.last_completed.nil?
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
# Whether a one-member audit may judge member's change to now_sha256: in git, and where the audit record gives
|
|
80
|
+
# no refusal, which Acknowledgement refuses by too, so list-member-problems offers no command certain to be
|
|
81
|
+
# refused (#626, #632).
|
|
82
|
+
#: (Member, String?) -> bool
|
|
83
|
+
def acknowledgeable?(member, now_sha256)
|
|
84
|
+
audit = @audit
|
|
85
|
+
@in_git && !audit.nil? && audit.acknowledgement_refusal(member, now_sha256:).nil?
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
# One map's members in project against what this tool last knew of each. A map that cannot be read is
|
|
90
|
+
# refused when it was asked for, and listed as broken among every map.
|
|
91
|
+
#: (Project, String, ?listing_all: bool) -> MemberProblems
|
|
92
|
+
def self.for(project, id, listing_all: false)
|
|
93
|
+
members = project.member_maps.find(id).members
|
|
94
|
+
new(members: members, now: project.current_by_key(members), baselines: Baselines.of(project, members))
|
|
95
|
+
rescue Error => e
|
|
96
|
+
raise unless listing_all
|
|
97
|
+
|
|
98
|
+
none = MapMembers.new(id, [])
|
|
99
|
+
new(members: none, now: {}, baselines: Baselines.of(project, none), unreadable: e.payload)
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
# now holds, for each member by key, its hash now or the refusal that says why it cannot be found.
|
|
103
|
+
# unreadable is the refusal for a map this tool could not read, which is listed as one broken entry.
|
|
104
|
+
#: (members: MapMembers, now: Hash[String, untyped],
|
|
105
|
+
#| baselines: Baselines, ?unreadable: Hash[String, untyped]?) -> void
|
|
106
|
+
def initialize(members:, now:, baselines:, unreadable: nil)
|
|
107
|
+
@members = members
|
|
108
|
+
@now = now
|
|
109
|
+
@baselines = baselines
|
|
110
|
+
@unreadable = unreadable
|
|
111
|
+
freeze
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
# Every member as one entry: its map, id and state, and for a changed member what it was compared with.
|
|
115
|
+
#: () -> Array[Hash[String, untyped]]
|
|
116
|
+
def entries
|
|
117
|
+
if @unreadable
|
|
118
|
+
return [{ "principle" => principle_id, "state" => "unreadable_map" }.merge(@unreadable.except("status"))]
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
@members.map { |member| entry(member) }
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
# The entries whose reference is broken.
|
|
125
|
+
#: () -> Array[Hash[String, untyped]]
|
|
126
|
+
def broken
|
|
127
|
+
entries.select { |each| MemberState::BROKEN.include?(each.fetch("state")) }
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
# Whether a member an audit or acknowledgement assessed changed since, rather than only a member edited since
|
|
131
|
+
# the commit that mapped it, which no audit assessed (D44).
|
|
132
|
+
#: () -> bool
|
|
133
|
+
def assessed_change? = changed_members.any? { |member| assessed?(member) }
|
|
134
|
+
|
|
135
|
+
# The entries whose own text changed since this tool last knew it, those an audit assessed first. Given a
|
|
136
|
+
# block, each gains "history", what the block makes of its member, the hash it was compared with, and its
|
|
137
|
+
# hash now.
|
|
138
|
+
#: () ?{ (Member, String, String) -> Hash[String, untyped] } -> Array[Hash[String, untyped]]
|
|
139
|
+
def changed(&history)
|
|
140
|
+
assessed, unassessed = changed_members.partition { |member| assessed?(member) }
|
|
141
|
+
(assessed + unassessed).map do |member|
|
|
142
|
+
listed = entry(member)
|
|
143
|
+
next listed unless history
|
|
144
|
+
|
|
145
|
+
listed.merge("history" => history.call(member, baseline(member.key).first.to_s, @now[member.key].to_s))
|
|
146
|
+
end
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
# Each member in paths whose own text changed or whose reference is now broken, with its state, as a completed
|
|
150
|
+
# audit names them for another map that shares a file the audit's fixes changed (#410).
|
|
151
|
+
#: (Array[String]) -> Array[MemberState]
|
|
152
|
+
def changed_or_broken_in(paths)
|
|
153
|
+
return [] if @unreadable
|
|
154
|
+
|
|
155
|
+
@members.in_files(paths).map { |member| MemberState.new(member.id, entry(member).fetch("state")) }
|
|
156
|
+
.select(&:changed_or_broken?)
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
# Each member no audit has judged as it is now, by id with its state: changed or broken since what this tool
|
|
160
|
+
# last knew of it, or never assessed, having only its mapping commit or no baseline at all (D71, D44). A map
|
|
161
|
+
# this tool cannot read is one entry, with no id, whose state is unreadable_map.
|
|
162
|
+
#: () -> Array[Hash[String, untyped]]
|
|
163
|
+
def unjudged = entries.reject { it.fetch("state") == "unchanged" }.map { it.slice("id", "state") }
|
|
164
|
+
|
|
165
|
+
#: () -> String
|
|
166
|
+
def principle_id = @members.principle_id
|
|
167
|
+
|
|
168
|
+
# The ids of the members whose own text changed and whose change a one-member audit may judge on its own, so
|
|
169
|
+
# list-member-problems fills that audit's command for each (D56, #728).
|
|
170
|
+
#: () -> Array[String]
|
|
171
|
+
def acknowledgeable_member_ids
|
|
172
|
+
changed_members.select { |member| @baselines.acknowledgeable?(member, @now[member.key]) }.map(&:id)
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
# How many members found in their files were compared with each baseline, audit or mapping_commit, or had
|
|
176
|
+
# none (no_baseline), so an unchanged member compared only with the commit that mapped it is not read as
|
|
177
|
+
# unchanged since an audit.
|
|
178
|
+
#: () -> Hash[String, Integer]
|
|
179
|
+
def compared_with
|
|
180
|
+
return {} if @unreadable
|
|
181
|
+
|
|
182
|
+
found = @members.reject { |member| @now[member.key].is_a?(Hash) }
|
|
183
|
+
found.map { |member| baseline(member.key).last&.fetch("baseline") || "no_baseline" }.tally.sort.to_h
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
# Whether the principle awaits its first full audit: none has completed and a member is found, in git one a
|
|
187
|
+
# commit or an open audit gives a baseline, since until a commit holds its map committing gives its members
|
|
188
|
+
# one (D58, #731 R7).
|
|
189
|
+
#: () -> bool
|
|
190
|
+
def awaits_first_audit?
|
|
191
|
+
return false unless @baselines.no_completed_audit?
|
|
192
|
+
|
|
193
|
+
bases = compared_with.keys
|
|
194
|
+
@baselines.in_git ? bases.any? { it != "no_baseline" } : !bases.empty?
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
# Why members found in their files have no baseline, or nil when every one has one. Outside git a principle
|
|
198
|
+
# awaiting its first full audit has none either: that audit is the one remedy, which awaits_first_audit? names.
|
|
199
|
+
#: () -> String?
|
|
200
|
+
def no_baseline_because
|
|
201
|
+
return if no_baseline_paths.empty? || (awaits_first_audit? && !@baselines.in_git)
|
|
202
|
+
|
|
203
|
+
@baselines.none_because
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
# Each file holding a member found with no baseline, once.
|
|
207
|
+
#: () -> Array[String]
|
|
208
|
+
def no_baseline_paths
|
|
209
|
+
return [] if @unreadable
|
|
210
|
+
|
|
211
|
+
@members.select { |member| !@now[member.key].is_a?(Hash) && baseline(member.key).last.nil? }
|
|
212
|
+
.map(&:path).uniq
|
|
213
|
+
end
|
|
214
|
+
|
|
215
|
+
private
|
|
216
|
+
|
|
217
|
+
# The members whose own text changed since this tool last knew it; none in a map that cannot be read.
|
|
218
|
+
#: () -> Array[Member]
|
|
219
|
+
def changed_members
|
|
220
|
+
return [] if @unreadable
|
|
221
|
+
|
|
222
|
+
@members.select { |member| entry(member).fetch("state") == "changed" }
|
|
223
|
+
end
|
|
224
|
+
|
|
225
|
+
#: (Member) -> bool
|
|
226
|
+
def assessed?(member) = baseline(member.key).last&.fetch("baseline") == "audit"
|
|
227
|
+
|
|
228
|
+
#: (Member) -> Hash[String, untyped]
|
|
229
|
+
def entry(member)
|
|
230
|
+
listed = { "principle" => principle_id, "id" => member.id }
|
|
231
|
+
now = @now[member.key]
|
|
232
|
+
return compared(listed, member.key, now.to_s) unless now.is_a?(Hash)
|
|
233
|
+
|
|
234
|
+
listed.merge("state" => LocateRefusal.new(now).member_state).merge(now.except("status"))
|
|
235
|
+
end
|
|
236
|
+
|
|
237
|
+
# listed, compared with the last audit's hash for key, or else with its hash at the commit that mapped it.
|
|
238
|
+
# An unchanged member compared only with the commit that mapped it has never been assessed, so it is
|
|
239
|
+
# counted apart from one an audit assessed: counts is read first, and "unchanged" alone reads as audited (#569).
|
|
240
|
+
#: (Hash[String, untyped], String, String) -> Hash[String, untyped]
|
|
241
|
+
def compared(listed, key, sha)
|
|
242
|
+
then_sha, since = baseline(key)
|
|
243
|
+
return listed.merge("state" => "no_baseline") unless since
|
|
244
|
+
return listed.merge("state" => "changed", "since" => since) unless then_sha == sha
|
|
245
|
+
|
|
246
|
+
listed.merge("state" => since["baseline"] == "mapping_commit" ? "unchanged_since_mapping" : "unchanged")
|
|
247
|
+
end
|
|
248
|
+
|
|
249
|
+
#: (String) -> [String?, Hash[String, untyped]?]
|
|
250
|
+
def baseline(key) = @baselines.of(key)
|
|
251
|
+
end
|
|
252
|
+
end
|
|
253
|
+
end
|