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,60 @@
|
|
|
1
|
+
# typed: strict
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require_relative "../error"
|
|
5
|
+
require_relative "decided_prior_findings"
|
|
6
|
+
require_relative "kept_prior_finding"
|
|
7
|
+
require_relative "prior_finding_decision"
|
|
8
|
+
|
|
9
|
+
module Principles
|
|
10
|
+
# The domain logic, and the mediation of persistence. models/CLAUDE.md owns the boundary.
|
|
11
|
+
module Models
|
|
12
|
+
# What the next full audit's decisions, once PriorFindings has checked them, do to each finding the last audit
|
|
13
|
+
# left open (D35): the findings kept, as the record keeps them, the ones closed, and, for each new finding that
|
|
14
|
+
# raises a prior one again, when that one was first raised.
|
|
15
|
+
class PriorFindingOutcomes
|
|
16
|
+
# decisions are the checked decisions, each on the finding it decides.
|
|
17
|
+
#: (Array[PriorFindingDecision]) -> void
|
|
18
|
+
def initialize(decisions)
|
|
19
|
+
@decisions = decisions
|
|
20
|
+
freeze
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# What the decisions do to each finding, which each kept finding answers for itself.
|
|
24
|
+
#: () -> DecidedPriorFindings
|
|
25
|
+
def decided
|
|
26
|
+
holding = of(["still_holds"]).map(&:kept)
|
|
27
|
+
DecidedPriorFindings.new(
|
|
28
|
+
{ "kept" => holding.map(&:to_h), "closed" => of(PriorFindingDecision::CLOSING).map(&:closed),
|
|
29
|
+
"inherited" => first_raised(of(["raised_again"])), "dropped" => holding.filter_map(&:dropped_write_up),
|
|
30
|
+
"waits_on_dropped" => holding.filter_map(&:waits_on_dropped),
|
|
31
|
+
"evidence_replaced" => holding.flat_map(&:swaps) }
|
|
32
|
+
)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
private
|
|
36
|
+
|
|
37
|
+
#: (Array[String]) -> Array[PriorFindingDecision]
|
|
38
|
+
def of(kinds) = @decisions.select { |decision| kinds.include?(decision.kind) }
|
|
39
|
+
|
|
40
|
+
# When each finding raised again was first raised, by the id of the new finding it became. Two raised as one
|
|
41
|
+
# new finding are refused, since only one first_raised_at can pass on.
|
|
42
|
+
#: (Array[PriorFindingDecision]) -> Hash[String, String?]
|
|
43
|
+
def first_raised(raised)
|
|
44
|
+
raised.group_by(&:raised_as).to_h do |as, same|
|
|
45
|
+
raised_once(as, same.map { |decision| decision.finding.id })
|
|
46
|
+
[as, same.fetch(0).finding.first_raised_at]
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
#: (String, Array[String]) -> void
|
|
51
|
+
def raised_once(as, ids)
|
|
52
|
+
return if ids.one?
|
|
53
|
+
|
|
54
|
+
raise Error.new("invalid_decision", "#{ids.join(", ")} are raised again as one new finding, #{as}",
|
|
55
|
+
"Raise one again as #{as} and close the others as withdrawn, with a reason naming #{as}, " \
|
|
56
|
+
"or raise each as its own new finding.")
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
# typed: strict
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require_relative "../error"
|
|
5
|
+
require_relative "audit_record"
|
|
6
|
+
require_relative "decided_prior_findings"
|
|
7
|
+
require_relative "prior_finding_outcomes"
|
|
8
|
+
require_relative "prior_finding_decision"
|
|
9
|
+
require_relative "member"
|
|
10
|
+
|
|
11
|
+
module Principles
|
|
12
|
+
# The domain logic, and the mediation of persistence. models/CLAUDE.md owns the boundary.
|
|
13
|
+
module Models
|
|
14
|
+
# The findings the last completed audit deferred or accepted, which the next full audit must decide one by
|
|
15
|
+
# one (D35): each still holds, was resolved, was withdrawn, or is raised again as a new finding. This tool
|
|
16
|
+
# lists them and checks the decisions; whether a finding still holds is the agent's judgement.
|
|
17
|
+
class PriorFindings
|
|
18
|
+
# record is the last audit's record, or nil; members are the map's members now, and now each one's hash.
|
|
19
|
+
#: (record: AuditRecord?, members: MapMembers, now: Hash[String, String]) -> void
|
|
20
|
+
def initialize(record:, members:, now:)
|
|
21
|
+
@record = record
|
|
22
|
+
@left_open = record&.left_open || [] #: Array[OpenFinding]
|
|
23
|
+
@members = members
|
|
24
|
+
@now = now
|
|
25
|
+
freeze
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# Each prior finding as the next audit's request lists it, under its members' ids now, with whether each
|
|
29
|
+
# member changed since that audit.
|
|
30
|
+
#: () -> Array[Hash[String, untyped]]
|
|
31
|
+
def listed
|
|
32
|
+
@left_open.map { |finding| finding.for_next_audit_against(@members, @now) }
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# Each prior finding as the request shows it: as listed, and with a last_audited_commit, each evidence item with
|
|
36
|
+
# what since says of it now against that commit (#858, #877). The request id digests listed, so these facts never
|
|
37
|
+
# make an answer stale.
|
|
38
|
+
#: () { (EvidenceItem, String) -> Hash[String, bool] } -> Array[Hash[String, untyped]]
|
|
39
|
+
def shown(&since)
|
|
40
|
+
base = last_audited_commit or return listed
|
|
41
|
+
|
|
42
|
+
@left_open.map { |finding| finding.for_next_audit_against(@members, @now) { |item| since.call(item, base) } }
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# The commit the audit that left these findings open, raising or keeping each, was based on: what each evidence
|
|
46
|
+
# item's text is compared at (#858). nil outside git, or with no record. The record AuditRecord#left_open reads
|
|
47
|
+
# them from gives it, so the two cannot disagree.
|
|
48
|
+
#: () -> String?
|
|
49
|
+
def last_audited_commit = @record&.last_completed&.base_commit
|
|
50
|
+
|
|
51
|
+
# What the request id digests, so an answer to a request made before the record changed is stale.
|
|
52
|
+
#: () -> Array[Hash[String, untyped]]
|
|
53
|
+
def digested
|
|
54
|
+
listed.map { |finding| finding.slice("finding_id", "member_ids", "problem", "action", "first_raised_at") }
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# The decisions, checked against the listed findings and this audit's new findings. locate checks that a
|
|
58
|
+
# new recorded_at resolves, as a deferral's is checked; records read the record a claim of an earlier
|
|
59
|
+
# first_raised_at cites (D53).
|
|
60
|
+
#: (untyped, Array[untyped], ?records: AuditRecords?) { (Locator) -> void } -> DecidedPriorFindings
|
|
61
|
+
def decided(decisions, findings, records: nil, &locate)
|
|
62
|
+
new_ids = findings.map { |finding| finding.fetch("id") }
|
|
63
|
+
decisions = checked(complete(decisions, left_open_by_id.keys), new_ids, locate, records)
|
|
64
|
+
PriorFindingOutcomes.new(decisions).decided.tap { it.distinct_from(new_ids) }
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
private
|
|
68
|
+
|
|
69
|
+
# The decisions, each on the finding it decides, once checked against it and this audit's new findings.
|
|
70
|
+
#: (Array[Hash[String, untyped]], Array[String], ^(Locator) -> void, AuditRecords?)
|
|
71
|
+
#| -> Array[PriorFindingDecision]
|
|
72
|
+
def checked(decisions, new_ids, locate, records)
|
|
73
|
+
open = left_open_by_id
|
|
74
|
+
decisions.each_with_index.map do |each, index|
|
|
75
|
+
decision = PriorFindingDecision.new(each, open.fetch(each["finding_id"]), new_ids,
|
|
76
|
+
at: "/prior_finding_decisions/#{index}", members: @members)
|
|
77
|
+
check(decision, locate, records)
|
|
78
|
+
decision
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
# Refuses decision unless it is sound for the finding it decides, given this audit's new findings, and any
|
|
83
|
+
# earlier first_raised_at it claims is borne out by the record it cites, which records read (D53).
|
|
84
|
+
#: (PriorFindingDecision, ^(Locator) -> void, AuditRecords?) -> void
|
|
85
|
+
def check(decision, locate, records)
|
|
86
|
+
decision.check(locate)
|
|
87
|
+
decision.first_raised_claim.check(records, principle_id)
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
# The principle whose record left the prior findings open; none is decided without one.
|
|
91
|
+
#: () -> String
|
|
92
|
+
def principle_id = @record ? @record.principle_id : ""
|
|
93
|
+
|
|
94
|
+
#: () -> Hash[String, OpenFinding]
|
|
95
|
+
def left_open_by_id = @left_open.to_h { |finding| [finding.id, finding] }
|
|
96
|
+
|
|
97
|
+
#: (untyped, Array[String]) -> Array[Hash[String, untyped]]
|
|
98
|
+
def complete(decisions, expected)
|
|
99
|
+
decisions = [] unless decisions.is_a?(Array)
|
|
100
|
+
decided = decisions.map { |decision| decision.is_a?(Hash) ? decision["finding_id"] : nil }
|
|
101
|
+
return decisions if decided.sort == expected.sort
|
|
102
|
+
|
|
103
|
+
raise Error.new("incomplete_prior_findings", "Decide each prior finding exactly once: #{expected.join(", ")}",
|
|
104
|
+
"Give prior_finding_decisions one {finding_id, decision} for each prior finding the request " \
|
|
105
|
+
"lists, with decision still_holds, resolved, withdrawn or raised_again.")
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
end
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
# typed: strict
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require_relative "../error"
|
|
5
|
+
require_relative "member_problems"
|
|
6
|
+
require_relative "missing_files"
|
|
7
|
+
|
|
8
|
+
module Principles
|
|
9
|
+
# The domain logic, which loads and saves itself through data/. models/CLAUDE.md owns the boundary.
|
|
10
|
+
module Models
|
|
11
|
+
# What list-member-problems reports across the maps it reads: refused while any member's reference is
|
|
12
|
+
# broken, so a gate can stop on its exit status alone, and otherwise every changed member and a count of
|
|
13
|
+
# each state.
|
|
14
|
+
class ProblemReport
|
|
15
|
+
# history, when given, lets each missing file carry the rename git detects for it (#276).
|
|
16
|
+
#: (Array[MemberProblems], ?history: ProjectHistory?) -> void
|
|
17
|
+
def initialize(maps, history: nil)
|
|
18
|
+
@maps = maps
|
|
19
|
+
@history = history
|
|
20
|
+
freeze
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# The changed members and the count of each state, refused when any member is broken. Given a block, each
|
|
24
|
+
# changed member gains the history the block makes of its map id, member, baseline hash and hash now.
|
|
25
|
+
#: () ?{ (String, Member, String, String) -> Hash[String, untyped] } -> Hash[String, untyped]
|
|
26
|
+
def checked(&)
|
|
27
|
+
broken = @maps.flat_map(&:broken)
|
|
28
|
+
refuse(broken) unless broken.empty?
|
|
29
|
+
|
|
30
|
+
{ "members" => changed(&),
|
|
31
|
+
"counts" => @maps.flat_map(&:entries).map { |each| each.fetch("state") }.tally.sort.to_h,
|
|
32
|
+
"compared_with" => compared_with, **no_baseline_because, **awaiting_first_audit_field }
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# The principles that await their first full audit, in map order, which list-member-problems names in one
|
|
36
|
+
# line (D58, #731).
|
|
37
|
+
#: () -> Array[String]
|
|
38
|
+
def awaiting_first_audit = @maps.select(&:awaits_first_audit?).map(&:principle_id)
|
|
39
|
+
|
|
40
|
+
# Whether any map's member changed since an audit or acknowledgement assessed it, not only since it was
|
|
41
|
+
# mapped, which --short exits 3 for rather than 4 (D44).
|
|
42
|
+
#: () -> bool
|
|
43
|
+
def assessed_change? = @maps.any?(&:assessed_change?)
|
|
44
|
+
|
|
45
|
+
# For each map with a changed member a one-member audit may judge, the ids of those members, which
|
|
46
|
+
# list-member-problems offers that audit for, one command each (#626, D56).
|
|
47
|
+
#: () -> Hash[String, Array[String]]
|
|
48
|
+
def acknowledgeable
|
|
49
|
+
@maps.to_h { |map| [map.principle_id, map.acknowledgeable_member_ids] }.reject { |_, ids| ids.empty? }
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# For each map with members no baseline holds, its map file and the files those members are in, and in git
|
|
53
|
+
# only those of them that differ from HEAD or are untracked, since committing an unchanged file does nothing
|
|
54
|
+
# (#710 X3). In git the list is empty when every one is committed already.
|
|
55
|
+
#: (MemberMaps) -> Hash[String, Array[String]]
|
|
56
|
+
def files_to_commit(member_maps)
|
|
57
|
+
no_baseline_paths.to_h do |id, paths|
|
|
58
|
+
files = [member_maps.file(id), *paths]
|
|
59
|
+
[id, @history&.inside_git? ? @history.uncommitted(files) : files]
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
private
|
|
64
|
+
|
|
65
|
+
# Each map's files holding a member with no baseline, by map id; a map with none is left out.
|
|
66
|
+
#: () -> Hash[String, Array[String]]
|
|
67
|
+
def no_baseline_paths
|
|
68
|
+
@maps.to_h { |map| [map.principle_id, map.no_baseline_paths] }.reject { |_, paths| paths.empty? }
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
#: () ?{ (String, Member, String, String) -> Hash[String, untyped] } -> Array[Hash[String, untyped]]
|
|
72
|
+
def changed(&history)
|
|
73
|
+
@maps.flat_map do |map|
|
|
74
|
+
next map.changed unless history
|
|
75
|
+
|
|
76
|
+
map.changed { |member, baseline, now| history.call(map.principle_id, member, baseline, now) }
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
# Each map's count of members by the baseline they were compared with; a map with none is left out.
|
|
81
|
+
#: () -> Hash[String, Hash[String, Integer]]
|
|
82
|
+
def compared_with
|
|
83
|
+
@maps.to_h { |map| [map.principle_id, map.compared_with] }.reject { |_, counts| counts.empty? }
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
# Why each map's members with no baseline have none, under no_baseline_because, or nothing when all have one.
|
|
87
|
+
#: () -> Hash[String, Hash[String, String]]
|
|
88
|
+
def no_baseline_because
|
|
89
|
+
because = @maps.to_h { |map| [map.principle_id, map.no_baseline_because] }.compact
|
|
90
|
+
because.empty? ? {} : { "no_baseline_because" => because }
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
# awaiting_first_audit under its own key, or nothing when no principle awaits one.
|
|
94
|
+
#: () -> Hash[String, Array[String]]
|
|
95
|
+
def awaiting_first_audit_field
|
|
96
|
+
awaiting = awaiting_first_audit
|
|
97
|
+
awaiting.empty? ? {} : { "awaiting_first_audit" => awaiting }
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
#: (Array[Hash[String, untyped]]) -> bot
|
|
101
|
+
def refuse(broken)
|
|
102
|
+
named = broken.map { |each| [each["principle"], each["id"]].compact.join(" ") }
|
|
103
|
+
missing = MissingFiles.new(broken, history: @history)
|
|
104
|
+
raise missing.refusal("#{broken.length} members cannot be found or read: #{named.join(", ")}",
|
|
105
|
+
rerun: "#{Error::EXECUTABLE} list-member-problems",
|
|
106
|
+
note: "A map listed as unreadable_map needs its own refusal's repair. ")
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
end
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
# typed: strict
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require_relative "../data/project_files"
|
|
5
|
+
require_relative "../data/schema_check"
|
|
6
|
+
require_relative "../data/map_files"
|
|
7
|
+
require_relative "../data/audit_files"
|
|
8
|
+
require_relative "../data/principles_subfolder"
|
|
9
|
+
require_relative "../data/repository"
|
|
10
|
+
require_relative "candidate_inventory"
|
|
11
|
+
require_relative "entry_files"
|
|
12
|
+
require_relative "member"
|
|
13
|
+
require_relative "member_file"
|
|
14
|
+
require_relative "hook_commands"
|
|
15
|
+
require_relative "loaded_files"
|
|
16
|
+
require_relative "located_text"
|
|
17
|
+
require_relative "member_loads"
|
|
18
|
+
require_relative "member_lines"
|
|
19
|
+
require_relative "member_maps"
|
|
20
|
+
require_relative "member_snapshots"
|
|
21
|
+
require_relative "missing_files"
|
|
22
|
+
require_relative "locate_refusal"
|
|
23
|
+
require_relative "audit_records"
|
|
24
|
+
require_relative "project_history"
|
|
25
|
+
require_relative "principles_folder_move"
|
|
26
|
+
|
|
27
|
+
module Principles
|
|
28
|
+
# The domain logic, and the mediation of persistence. models/CLAUDE.md owns the boundary.
|
|
29
|
+
module Models
|
|
30
|
+
# The user's project as this tool reads it: its instruction files, each member's text, the answers an
|
|
31
|
+
# agent hands in, and the maps, audit records and git history kept beside them. Controllers reach the
|
|
32
|
+
# project only through this model. It is handed its four data adapters, so a spec can hand it stand-ins
|
|
33
|
+
# that meet the same contracts (#337).
|
|
34
|
+
class Project
|
|
35
|
+
# The project at root, with its maps, audit records and history.
|
|
36
|
+
#: (String) -> Project
|
|
37
|
+
def self.at(root)
|
|
38
|
+
files = Data::ProjectFiles.new(root)
|
|
39
|
+
new(files: files, maps: Data::MapFiles.new(Data::PrinciplesSubfolder.new(files.root, "maps")),
|
|
40
|
+
audits: Data::AuditFiles.new(Data::PrinciplesSubfolder.new(files.root, "audits")),
|
|
41
|
+
repository: Data::Repository.new(files.root))
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
#: (files: Data::ProjectFiles, maps: Data::MapFiles, audits: Data::AuditFiles,
|
|
45
|
+
#| repository: Data::Repository) -> void
|
|
46
|
+
def initialize(files:, maps:, audits:, repository:)
|
|
47
|
+
@files = files
|
|
48
|
+
@maps = maps
|
|
49
|
+
@audits = audits
|
|
50
|
+
@repository = repository
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# The project's root, resolved.
|
|
54
|
+
#: () -> String
|
|
55
|
+
def root
|
|
56
|
+
@files.root
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
#: () -> MemberMaps
|
|
60
|
+
def member_maps
|
|
61
|
+
refuse_unmoved_folder
|
|
62
|
+
MemberMaps.new(@maps)
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
#: () -> AuditRecords
|
|
66
|
+
def audit_records
|
|
67
|
+
refuse_unmoved_folder
|
|
68
|
+
AuditRecords.new(@audits, history)
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
#: () -> ProjectHistory
|
|
72
|
+
def history
|
|
73
|
+
ProjectHistory.new(@repository, @files)
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
# Whether two paths name one file, as their symbolic links lead them, so an edit through either is an edit
|
|
77
|
+
# to both (#695).
|
|
78
|
+
#: (String, String) -> bool
|
|
79
|
+
def same_file?(path, other) = @files.leads_to(path) == @files.leads_to(other)
|
|
80
|
+
|
|
81
|
+
# Where a member is in its file now, and the hash of what it says there.
|
|
82
|
+
#: (Locator) -> Hash[String, untyped]
|
|
83
|
+
def locate(locator)
|
|
84
|
+
member_file(locator.path).locate(locator)
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
# The text locator finds now, as one value: its path, its lines and the hash of what it says (#909).
|
|
88
|
+
#: (Locator) -> LocatedText
|
|
89
|
+
def located_text(locator) = LocatedText.of(locate(locator))
|
|
90
|
+
|
|
91
|
+
# The member at locator, its lines in its file at commit beside its lines now, which an audit shows the
|
|
92
|
+
# agent so it judges the member's own change without the rest of its file's (#379); nil when commit lacks the
|
|
93
|
+
# file or its locator finds nothing there, as after a move, which each caller says in its own terms (#459).
|
|
94
|
+
# then_locator places it at commit where it was elsewhere then, as before a move (#547).
|
|
95
|
+
#: (Locator, since: String, ?then_locator: Locator) -> MemberLines?
|
|
96
|
+
def member_lines(locator, since:, then_locator: locator)
|
|
97
|
+
before = history.lines_at(since, then_locator) or return nil
|
|
98
|
+
|
|
99
|
+
MemberLines.new(at_baseline: before, now: member_file(locator.path).lines_of(locator))
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
# As member_lines, since the last full audit's commit, or that it cannot be found there, so a full audit still
|
|
103
|
+
# starts (#457).
|
|
104
|
+
#: (Locator, since: String) -> (MemberLines | MemberLinesNotFoundAtLastAudit)
|
|
105
|
+
def member_lines_since_last_audit(locator, since:)
|
|
106
|
+
member_lines(locator, since: since) || MemberLinesNotFoundAtLastAudit.new
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
# The project file at path as the type that finds a member in it, refused when it cannot be read.
|
|
110
|
+
#: (String) -> MemberFile
|
|
111
|
+
def member_file(path)
|
|
112
|
+
MemberFile.for(path: path, text: @files.read_text(path))
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
# Each of map's members by id, where it is in its file now and the hash of what it says there. It is
|
|
116
|
+
# refused once, listing every member that cannot be found or read, so the agent repairs them together.
|
|
117
|
+
#: (MemberMap) -> MemberSnapshots
|
|
118
|
+
def snapshots(map)
|
|
119
|
+
broken = [] #: Array[Hash[String, untyped]]
|
|
120
|
+
found = map.members.to_h do |member|
|
|
121
|
+
[member.id, locate(member.locator)]
|
|
122
|
+
rescue Error => e
|
|
123
|
+
broken << { "id" => member.id }.merge(e.payload.except("status"))
|
|
124
|
+
[member.id, {}]
|
|
125
|
+
end
|
|
126
|
+
broken.empty? ? MemberSnapshots.new(found) : refuse_broken(map.id, broken)
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
# The superseded, git-ignored file where an older version kept audit runs; this version never reads it.
|
|
130
|
+
OLDER_STATE = ".memeplex/state.json" #: String
|
|
131
|
+
|
|
132
|
+
# The files beside the maps that an older version wrote and this one does not read, so an audit kept
|
|
133
|
+
# only there is no baseline.
|
|
134
|
+
#: () -> Array[String]
|
|
135
|
+
def unread_older_files
|
|
136
|
+
@files.file?(OLDER_STATE) ? [OLDER_STATE] : []
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
# Each member's hash now by key, or the refusal that says why its text cannot be found or read.
|
|
140
|
+
#: (MapMembers) -> Hash[String, untyped]
|
|
141
|
+
def current_by_key(members)
|
|
142
|
+
members.to_h do |member|
|
|
143
|
+
[member.key, locate(member.locator).fetch("sha256")]
|
|
144
|
+
rescue Error => e
|
|
145
|
+
[member.key, e.payload]
|
|
146
|
+
end
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
#: (String, Array[Hash[String, untyped]]) -> bot
|
|
150
|
+
def refuse_broken(map_id, broken)
|
|
151
|
+
ids = broken.map { |member| member.fetch("id") }.join(", ")
|
|
152
|
+
missing = MissingFiles.new(broken, principle: map_id, history: history)
|
|
153
|
+
raise missing.refusal("#{broken.length} of #{map_id}'s members cannot be found or read: #{ids}")
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
# A member's text now and how fresh it is, which the block says from its hash. A member whose place
|
|
157
|
+
# in its file is gone is locator_lost; one whose file cannot be read is unavailable. Both keep the
|
|
158
|
+
# refusal.
|
|
159
|
+
#: (Locator) { (String) -> String } -> Hash[String, untyped]
|
|
160
|
+
def freshness(locator, &)
|
|
161
|
+
now = locate(locator)
|
|
162
|
+
{ "snapshot" => now, "freshness" => yield(now.fetch("sha256")) }
|
|
163
|
+
rescue Error => e
|
|
164
|
+
{ "freshness" => LocateRefusal.new(e.payload).member_state, "error" => e.payload }
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
# Each member's MemberLoads by id: when each host loads it into context, with the size of what loads, and
|
|
168
|
+
# the project hooks whose commands name its file, read once for them all unless the caller already read them.
|
|
169
|
+
#: (MapMembers, ?HookCommands) -> Hash[String, MemberLoads]
|
|
170
|
+
def member_loads(members, hooks = hook_commands)
|
|
171
|
+
members.to_h { |member| [member.id, MemberLoads.read(@files, member.locator, hooks: hooks)] }
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
# The files members load, each read whole once, with the project's hooks as hooks read them (#483).
|
|
175
|
+
#: (MapMembers, HookCommands) -> LoadedFiles
|
|
176
|
+
def loaded_files(members, hooks) = LoadedFiles.read(@files, members, hooks: hooks)
|
|
177
|
+
|
|
178
|
+
#: () -> HookCommands
|
|
179
|
+
def hook_commands
|
|
180
|
+
HookCommands.read(@files)
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
# The root entry files every session of a host loads, whether one links the other and which git does not
|
|
184
|
+
# track.
|
|
185
|
+
#: () -> EntryFiles
|
|
186
|
+
def entry_files = EntryFiles.read(@files, history)
|
|
187
|
+
|
|
188
|
+
ANSWER_REPAIR = "Repair each field listed, starting from the request's response_template."
|
|
189
|
+
|
|
190
|
+
# An answer to a phase's request from file, where - means it was read from stdin, checked against the
|
|
191
|
+
# phase's answer schema (D29).
|
|
192
|
+
#: (String, stdin: String?, phase: String) -> untyped
|
|
193
|
+
def answer(file, stdin:, phase:)
|
|
194
|
+
parsed = file == "-" ? @files.parse_json(stdin.to_s, "-") : @files.read_json(file)
|
|
195
|
+
Data::SchemaCheck.new("#{phase}-answer").checked(parsed, code: "invalid_answer", source: file,
|
|
196
|
+
repair: ANSWER_REPAIR)
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
# What list-candidate-files found: the candidate files as it lists them, and which git does not track.
|
|
200
|
+
#: () -> CandidateInventory
|
|
201
|
+
def candidate_files
|
|
202
|
+
CandidateInventory.found(@files.inventory, history)
|
|
203
|
+
end
|
|
204
|
+
|
|
205
|
+
private
|
|
206
|
+
|
|
207
|
+
# Refuses while the maps or records are still in the folder's earlier name (#536).
|
|
208
|
+
#: () -> void
|
|
209
|
+
def refuse_unmoved_folder
|
|
210
|
+
PrinciplesFolderMove.new(@maps.held_in | @audits.held_in).refuse_while_pending
|
|
211
|
+
end
|
|
212
|
+
end
|
|
213
|
+
end
|
|
214
|
+
end
|