hecks 5.0.0 → 7.0.0
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 +4 -4
- data/examples/pizzas/bluebook/pizzas.bluebook +1 -0
- data/exe/hecks +31 -12
- data/lib/hecks/adapters/driven/claude_code.rb +3 -3
- data/lib/hecks/adapters/driven/d1.rb +2 -1
- data/lib/hecks/adapters/driven/folder.rb +44 -28
- data/lib/hecks/adapters/driven/folder_overlay.rb +66 -0
- data/lib/hecks/adapters/driven/google_authentication.adapter +3 -0
- data/lib/hecks/adapters/driven/google_authentication.rb +99 -0
- data/lib/hecks/adapters/driven/governance_authorization.rb +32 -36
- data/lib/hecks/adapters/driven/heki/journal.rb +45 -0
- data/lib/hecks/adapters/driven/heki.rb +1 -1
- data/lib/hecks/adapters/driven/identity_registry.rb +1 -1
- data/lib/hecks/adapters/driven/in_memory_ordering.rb +3 -2
- data/lib/hecks/adapters/driven/in_process_key_vault.rb +1 -1
- data/lib/hecks/adapters/driven/key_keeper.rb +42 -0
- data/lib/hecks/adapters/driven/lambda/client.rb +8 -2
- data/lib/hecks/adapters/driven/lambda/remote_dispatcher.rb +185 -0
- data/lib/hecks/adapters/driven/lambda.rb +14 -2
- data/lib/hecks/adapters/driven/memory/dispatch_claims.rb +25 -0
- data/lib/hecks/adapters/driven/memory/outbox_rows.rb +2 -2
- data/lib/hecks/adapters/driven/memory.rb +15 -4
- data/lib/hecks/adapters/driven/postgres/codec.rb +1 -1
- data/lib/hecks/adapters/driven/postgres/journal.rb +46 -0
- data/lib/hecks/adapters/driven/postgres/name_guard.rb +25 -0
- data/lib/hecks/adapters/driven/postgres/outbox.rb +33 -7
- data/lib/hecks/adapters/driven/postgres/projection.rb +43 -0
- data/lib/hecks/adapters/driven/postgres/repository.rb +2 -2
- data/lib/hecks/adapters/driven/postgres/schema_builder.rb +2 -1
- data/lib/hecks/adapters/driven/postgres/set_dialect.rb +44 -0
- data/lib/hecks/adapters/driven/postgres/shared_connection.rb +1 -1
- data/lib/hecks/adapters/driven/postgres.adapter +8 -0
- data/lib/hecks/adapters/driven/postgres.rb +11 -3
- data/lib/hecks/adapters/driven/postgres_dump/connection.rb +57 -0
- data/lib/hecks/adapters/driven/postgres_dump.rb +117 -0
- data/lib/hecks/adapters/driven/postgres_era/boot.rb +58 -0
- data/lib/hecks/{ports/persistence/plugins/era → adapters/driven}/postgres_era/class_methods.rb +28 -8
- data/lib/hecks/{ports/persistence/plugins/era → adapters/driven}/postgres_era/dialect.rb +11 -6
- data/lib/hecks/adapters/driven/postgres_era/field_cache_provisioner.rb +39 -0
- data/lib/hecks/{ports/persistence/plugins/era → adapters/driven}/postgres_era/journal.rb +52 -13
- data/lib/hecks/{ports/persistence/plugins/era → adapters/driven}/postgres_era/lineage/chain_sql.rb +37 -53
- data/lib/hecks/adapters/driven/postgres_era/lineage/direct_sql.rb +73 -0
- data/lib/hecks/adapters/driven/postgres_era/lineage/direct_verified.json +10 -0
- data/lib/hecks/adapters/driven/postgres_era/lineage/direct_verified.rb +65 -0
- data/lib/hecks/adapters/driven/postgres_era/lineage/domain_rename.rb +143 -0
- data/lib/hecks/adapters/driven/postgres_era/lineage/domain_rename_state.rb +108 -0
- data/lib/hecks/{ports/persistence/plugins/era → adapters/driven}/postgres_era/lineage/era_integrity.rb +13 -8
- data/lib/hecks/adapters/driven/postgres_era/lineage/era_store.rb +125 -0
- data/lib/hecks/adapters/driven/postgres_era/lineage/layered_sql.rb +64 -0
- data/lib/hecks/adapters/driven/postgres_era/lineage/ledger_reset.rb +150 -0
- data/lib/hecks/adapters/driven/postgres_era/lineage/mint_provisioning.rb +85 -0
- data/lib/hecks/{ports/persistence/plugins/era → adapters/driven}/postgres_era/lineage/mint_transaction.rb +13 -9
- data/lib/hecks/{ports/persistence/plugins/era → adapters/driven}/postgres_era/lineage/provisioning.rb +11 -5
- data/lib/hecks/adapters/driven/postgres_era/lineage/split_sql.rb +76 -0
- data/lib/hecks/{ports/persistence/plugins/era → adapters/driven}/postgres_era/lineage/sql_templates.rb +15 -2
- data/lib/hecks/{ports/persistence/plugins/era → adapters/driven}/postgres_era/lineage/tail_merge.rb +1 -1
- data/lib/hecks/{ports/persistence/plugins/era → adapters/driven}/postgres_era/lineage.rb +11 -3
- data/lib/hecks/adapters/driven/postgres_era/lineage_manager/context.rb +68 -0
- data/lib/hecks/{ports/persistence/plugins/era → adapters/driven}/postgres_era/lineage_manager/coverage_check.rb +16 -12
- data/lib/hecks/adapters/driven/postgres_era/lineage_manager/disposable_ledger.rb +119 -0
- data/lib/hecks/adapters/driven/postgres_era/lineage_manager/era_resolution.rb +219 -0
- data/lib/hecks/adapters/driven/postgres_era/lineage_manager/era_resolver.rb +114 -0
- data/lib/hecks/adapters/driven/postgres_era/lineage_manager/held_era.rb +68 -0
- data/lib/hecks/adapters/driven/postgres_era/lineage_manager/ledger_change.rb +63 -0
- data/lib/hecks/{ports/persistence/plugins/era → adapters/driven}/postgres_era/lineage_manager/merge_coordinator.rb +3 -3
- data/lib/hecks/adapters/driven/postgres_era/lineage_manager/mint_decision.rb +163 -0
- data/lib/hecks/adapters/driven/postgres_era/lineage_manager/mint_request.rb +24 -0
- data/lib/hecks/adapters/driven/postgres_era/lineage_manager/minter.rb +143 -0
- data/lib/hecks/adapters/driven/postgres_era/lineage_manager/refusals.rb +76 -0
- data/lib/hecks/{ports/persistence/plugins/era → adapters/driven}/postgres_era/lineage_manager.rb +46 -12
- data/lib/hecks/{ports/persistence/plugins/era → adapters/driven}/postgres_era/query_phases.rb +8 -2
- data/lib/hecks/adapters/driven/postgres_era/shared_tables.rb +32 -0
- data/lib/hecks/adapters/driven/postgres_era.adapter +19 -1
- data/lib/hecks/{ports/persistence/plugins/era → adapters/driven}/postgres_era.rb +39 -23
- data/lib/hecks/adapters/driven/prism.rb +4 -4
- data/lib/hecks/adapters/driven/projection/read_model_walk.rb +126 -0
- data/lib/hecks/adapters/driven/projection/row_decoding.rb +57 -0
- data/lib/hecks/adapters/driven/set_transform/assignments.rb +101 -0
- data/lib/hecks/adapters/driven/set_transform/expressions.rb +138 -0
- data/lib/hecks/adapters/driven/set_transform.rb +81 -0
- data/lib/hecks/adapters/driven/sql_keyset.rb +61 -0
- data/lib/hecks/adapters/driven/sql_query_builder.rb +29 -9
- data/lib/hecks/adapters/driven/sqlite/codec.rb +1 -1
- data/lib/hecks/adapters/driven/sqlite/ddl.rb +7 -0
- data/lib/hecks/adapters/driven/sqlite/dialect.rb +2 -1
- data/lib/hecks/adapters/driven/sqlite/dispatch_claims.rb +28 -0
- data/lib/hecks/adapters/driven/sqlite/journal.rb +46 -0
- data/lib/hecks/adapters/driven/sqlite/name_guard.rb +25 -0
- data/lib/hecks/adapters/driven/sqlite/outbox.rb +5 -4
- data/lib/hecks/adapters/driven/sqlite/projection.rb +9 -135
- data/lib/hecks/adapters/driven/sqlite/repository.rb +2 -2
- data/lib/hecks/adapters/driven/sqlite/schema_builder.rb +36 -4
- data/lib/hecks/adapters/driven/sqlite/set_dialect.rb +39 -0
- data/lib/hecks/adapters/driven/sqlite.adapter +2 -0
- data/lib/hecks/adapters/driven/sqlite.rb +20 -3
- data/lib/hecks/adapters/driven/tenant_provisioner.rb +1 -1
- data/lib/hecks/adapters/driven/worktree_overlay.rb +117 -0
- data/lib/hecks/adapters/driven.rb +8 -3
- data/lib/hecks/adapters/driving/cli/shorthand.rb +5 -1
- data/lib/hecks/adapters/driving/cli.rb +2 -2
- data/lib/hecks/adapters/driving/cli_runner/answers.rb +96 -34
- data/lib/hecks/adapters/driving/cli_runner/bang_rule.rb +35 -0
- data/lib/hecks/adapters/driving/cli_runner/completion_words.rb +36 -0
- data/lib/hecks/adapters/driving/cli_runner/json_words.rb +55 -0
- data/lib/hecks/adapters/driving/cli_runner/name_index.rb +63 -0
- data/lib/hecks/adapters/driving/cli_runner/read_context.rb +32 -0
- data/lib/hecks/adapters/driving/cli_runner/renamed_names.rb +49 -0
- data/lib/hecks/adapters/driving/cli_runner/resolution.rb +66 -39
- data/lib/hecks/adapters/driving/cli_runner/resume_words.rb +63 -0
- data/lib/hecks/adapters/driving/cli_runner/run_reference.rb +66 -0
- data/lib/hecks/adapters/driving/cli_runner/run_replay.rb +118 -0
- data/lib/hecks/adapters/driving/cli_runner/search_words.rb +35 -0
- data/lib/hecks/adapters/driving/cli_runner/settling.rb +14 -9
- data/lib/hecks/adapters/driving/cli_runner/streaming.rb +12 -11
- data/lib/hecks/adapters/driving/cli_runner/suggestions.rb +16 -8
- data/lib/hecks/adapters/driving/cli_runner/tour_words.rb +77 -0
- data/lib/hecks/adapters/driving/cli_runner/verb_names.rb +81 -0
- data/lib/hecks/adapters/driving/cli_runner/verbosity.rb +75 -0
- data/lib/hecks/adapters/driving/cli_runner.rb +110 -9
- data/lib/hecks/adapters/driving/context_line.rb +85 -0
- data/lib/hecks/adapters/driving/exit_status.rb +31 -0
- data/lib/hecks/adapters/driving/github_webhook.rb +2 -1
- data/lib/hecks/adapters/driving/handle/verb_methods.rb +46 -0
- data/lib/hecks/adapters/driving/handle.rb +9 -17
- data/lib/hecks/adapters/driving/json.rb +11 -8
- data/lib/hecks/adapters/driving/launcher_options.rb +41 -34
- data/lib/hecks/adapters/driving/mcp/progress_sink.rb +45 -0
- data/lib/hecks/adapters/driving/mcp/tool_calls.rb +7 -5
- data/lib/hecks/adapters/driving/mcp.rb +25 -5
- data/lib/hecks/adapters/driving/mcp_scope/argument_policy.rb +40 -7
- data/lib/hecks/adapters/driving/mcp_scope/env_problems.rb +3 -1
- data/lib/hecks/adapters/driving/mcp_scope.rb +11 -9
- data/lib/hecks/adapters/driving/newcomer.rb +6 -5
- data/lib/hecks/adapters/driving/ruby/aggregate_module/hooks.rb +154 -0
- data/lib/hecks/adapters/driving/ruby/aggregate_module.rb +73 -101
- data/lib/hecks/adapters/driving/ruby/bulk_verbs.rb +127 -0
- data/lib/hecks/adapters/driving/ruby/{chapter.rb → declaration.rb} +27 -27
- data/lib/hecks/adapters/driving/ruby/query_rows.rb +27 -0
- data/lib/hecks/adapters/driving/ruby.rb +25 -24
- data/lib/hecks/adapters/driving/usage_cache.rb +14 -3
- data/lib/hecks/adapters/driving/usage_log.rb +146 -0
- data/lib/hecks/adapters/driving/write_guard.rb +55 -0
- data/lib/hecks/adapters/driving.rb +0 -1
- data/lib/hecks/assembly.rb +101 -0
- data/lib/hecks/behaviors/dsl.rb +18 -4
- data/lib/hecks/behaviors/expectations/boot.rb +7 -7
- data/lib/hecks/behaviors/expectations/checks.rb +1 -1
- data/lib/hecks/behaviors/expectations/dispatching.rb +10 -9
- data/lib/hecks/behaviors/expectations/qualify.rb +9 -9
- data/lib/hecks/behaviors/expectations.rb +24 -10
- data/lib/hecks/behaviors/ir.rb +4 -2
- data/lib/hecks/behaviors/rspec.rb +1 -1
- data/lib/hecks/behaviors/runner.rb +31 -4
- data/lib/hecks/bench/environment.rb +6 -2
- data/lib/hecks/bench/postgres_probe.rb +3 -1
- data/lib/hecks/bench/report.rb +4 -1
- data/lib/hecks/bench/suite.rb +9 -3
- data/lib/hecks/bluebook/acknowledgements.rb +81 -0
- data/lib/hecks/bluebook/aggregate.rb +29 -17
- data/lib/hecks/bluebook/ask_resolution.rb +17 -17
- data/lib/hecks/bluebook/assembly/aggregate_assembly.rb +8 -5
- data/lib/hecks/bluebook/assembly/contracts/sagas.rb +2 -0
- data/lib/hecks/bluebook/assembly/contracts/structure.rb +24 -15
- data/lib/hecks/bluebook/assembly/marks.rb +10 -1
- data/lib/hecks/bluebook/assembly/scalars.rb +8 -2
- data/lib/hecks/bluebook/assembly/specializer.rb +2 -2
- data/lib/hecks/bluebook/assembly.rb +10 -10
- data/lib/hecks/bluebook/behaviour/aggregate.rb +29 -4
- data/lib/hecks/bluebook/behaviour/attribute.rb +12 -0
- data/lib/hecks/bluebook/behaviour/command.rb +1 -1
- data/lib/hecks/bluebook/behaviour/{chapter.rb → declaration.rb} +17 -17
- data/lib/hecks/bluebook/behaviour/domain_port.rb +1 -1
- data/lib/hecks/bluebook/behaviour/entity.rb +1 -0
- data/lib/hecks/bluebook/behaviour/hecksagon.rb +8 -1
- data/lib/hecks/bluebook/behaviour/process_manager.rb +4 -1
- data/lib/hecks/bluebook/behaviour/query.rb +5 -0
- data/lib/hecks/bluebook/behaviour/read_model.rb +4 -1
- data/lib/hecks/bluebook/behaviour/traits.rb +13 -0
- data/lib/hecks/bluebook/capabilities/attribute_values.rb +17 -16
- data/lib/hecks/bluebook/capabilities/package_declared.rb +65 -0
- data/lib/hecks/bluebook/capabilities.rb +32 -132
- data/lib/hecks/bluebook/command.rb +33 -7
- data/lib/hecks/bluebook/context_map.rb +104 -0
- data/lib/hecks/bluebook/{chapter.rb → declaration.rb} +45 -17
- data/lib/hecks/bluebook/domain_port.rb +7 -3
- data/lib/hecks/bluebook/dsl/adapter_builder.rb +28 -4
- data/lib/hecks/bluebook/dsl/aggregate_builder/draining.rb +2 -2
- data/lib/hecks/bluebook/dsl/aggregate_builder/one_of_reference.rb +54 -0
- data/lib/hecks/bluebook/dsl/aggregate_builder/query_field_sealing.rb +11 -2
- data/lib/hecks/bluebook/dsl/aggregate_builder/query_sealing.rb +15 -5
- data/lib/hecks/bluebook/dsl/aggregate_builder/relationships.rb +21 -5
- data/lib/hecks/bluebook/dsl/aggregate_builder/renaming.rb +25 -0
- data/lib/hecks/bluebook/dsl/aggregate_builder/rules.rb +22 -22
- data/lib/hecks/bluebook/dsl/aggregate_builder/sealing.rb +2 -2
- data/lib/hecks/bluebook/dsl/aggregate_builder.rb +30 -22
- data/lib/hecks/bluebook/dsl/attribute_collector.rb +0 -1
- data/lib/hecks/bluebook/dsl/audience_word.rb +40 -0
- data/lib/hecks/bluebook/dsl/bluebook_builder/metadata.rb +56 -0
- data/lib/hecks/bluebook/dsl/bluebook_builder/modules.rb +80 -0
- data/lib/hecks/bluebook/dsl/bluebook_builder/pending_givens.rb +22 -22
- data/lib/hecks/bluebook/dsl/bluebook_builder/validation/computed_sources.rb +27 -21
- data/lib/hecks/bluebook/dsl/bluebook_builder/validation/correlation_keys.rb +54 -5
- data/lib/hecks/bluebook/dsl/bluebook_builder/validation/event_shapes.rb +5 -4
- data/lib/hecks/bluebook/dsl/bluebook_builder/validation/fan_out_rows.rb +3 -3
- data/lib/hecks/bluebook/dsl/bluebook_builder/validation/hop_tails.rb +13 -5
- data/lib/hecks/bluebook/dsl/bluebook_builder/validation/projected_fields.rb +4 -4
- data/lib/hecks/bluebook/dsl/bluebook_builder/validation/provisions.rb +38 -84
- data/lib/hecks/bluebook/dsl/bluebook_builder/validation/query_hops.rb +5 -5
- data/lib/hecks/bluebook/dsl/bluebook_builder/validation/references.rb +6 -6
- data/lib/hecks/bluebook/dsl/bluebook_builder/validation/with_projections.rb +6 -6
- data/lib/hecks/bluebook/dsl/bluebook_builder/validation.rb +4 -3
- data/lib/hecks/bluebook/dsl/bluebook_builder.rb +75 -92
- data/lib/hecks/bluebook/dsl/bootstrap_table.rb +10 -0
- data/lib/hecks/bluebook/dsl/command_builder/folded_sets.rb +48 -0
- data/lib/hecks/bluebook/dsl/command_builder.rb +39 -16
- data/lib/hecks/bluebook/dsl/context_map_builder.rb +73 -0
- data/lib/hecks/bluebook/dsl/entity_builder/rules.rb +17 -17
- data/lib/hecks/bluebook/dsl/entity_builder/scope.rb +3 -3
- data/lib/hecks/bluebook/dsl/entity_builder/sealing.rb +2 -2
- data/lib/hecks/bluebook/dsl/entity_builder.rb +3 -5
- data/lib/hecks/bluebook/dsl/generic_dispatch.rb +6 -4
- data/lib/hecks/bluebook/dsl/hecksagon_builder/{chapter_scope.rb → bluebook_scope.rb} +14 -14
- data/lib/hecks/bluebook/dsl/hecksagon_builder.rb +33 -27
- data/lib/hecks/bluebook/dsl/identity_declaration.rb +6 -2
- data/lib/hecks/bluebook/dsl/kinds.rb +53 -0
- data/lib/hecks/bluebook/dsl/lifecycle_builder.rb +4 -1
- data/lib/hecks/bluebook/dsl/port_operation_builder.rb +9 -2
- data/lib/hecks/bluebook/dsl/process_manager_builder.rb +25 -15
- data/lib/hecks/bluebook/dsl/query_builder.rb +14 -8
- data/lib/hecks/bluebook/dsl/read_model_builder/sealing.rb +6 -11
- data/lib/hecks/bluebook/dsl/read_model_builder.rb +1 -2
- data/lib/hecks/bluebook/dsl/translation_builder.rb +16 -1
- data/lib/hecks/bluebook/dsl/word_gate.rb +3 -2
- data/lib/hecks/bluebook/dsl/world_builder.rb +2 -2
- data/lib/hecks/bluebook/dsl.rb +4 -2
- data/lib/hecks/bluebook/expression/ast_json/emitters.rb +4 -0
- data/lib/hecks/bluebook/expression/ast_json.rb +5 -3
- data/lib/hecks/bluebook/expression/ast_reader/blocks.rb +41 -0
- data/lib/hecks/bluebook/expression/ast_reader.rb +2 -10
- data/lib/hecks/bluebook/expression/canonical_form.rb +9 -4
- data/lib/hecks/bluebook/expression/evaluator/parser.rb +4 -1
- data/lib/hecks/bluebook/expression/evaluator.rb +13 -9
- data/lib/hecks/bluebook/expression/projection.json +6 -0
- data/lib/hecks/bluebook/expression/resolver/block_predicates.rb +50 -2
- data/lib/hecks/bluebook/expression/resolver/lookups.rb +2 -1
- data/lib/hecks/bluebook/expression/resolver/operations.rb +1 -0
- data/lib/hecks/bluebook/expression/resolver/parsing.rb +21 -15
- data/lib/hecks/bluebook/expression/resolver/scanning.rb +4 -1
- data/lib/hecks/bluebook/expression.rb +1 -1
- data/lib/hecks/bluebook/hecksagon.rb +48 -23
- data/lib/hecks/bluebook/meta_validator/adapter_judge.rb +4 -1
- data/lib/hecks/bluebook/meta_validator/judge/cells.rb +1 -1
- data/lib/hecks/bluebook/meta_validator/judge/identity.rb +11 -5
- data/lib/hecks/bluebook/meta_validator/modes.rb +27 -27
- data/lib/hecks/bluebook/meta_validator/plan/category.rb +10 -2
- data/lib/hecks/bluebook/meta_validator/plan/verbs.rb +9 -0
- data/lib/hecks/bluebook/meta_validator/plan.rb +7 -6
- data/lib/hecks/bluebook/meta_validator/readings/fields.rb +3 -3
- data/lib/hecks/bluebook/meta_validator/reconstruction/heads.rb +14 -4
- data/lib/hecks/bluebook/meta_validator/reconstruction/reading.rb +8 -1
- data/lib/hecks/bluebook/meta_validator/reconstruction.rb +24 -19
- data/lib/hecks/bluebook/meta_validator/shapes.rb +9 -1
- data/lib/hecks/bluebook/meta_validator/syntax_boot/disk_cache.rb +19 -16
- data/lib/hecks/bluebook/meta_validator/syntax_boot.rb +21 -15
- data/lib/hecks/bluebook/meta_validator/verdict_cache/prebuilt.rb +4 -1
- data/lib/hecks/bluebook/meta_validator/verdict_cache.rb +11 -10
- data/lib/hecks/bluebook/meta_validator.rb +25 -22
- data/lib/hecks/bluebook/model_check/aggregate_checks.rb +95 -0
- data/lib/hecks/bluebook/model_check/ask_checks.rb +31 -5
- data/lib/hecks/bluebook/model_check/client_profile.rb +8 -42
- data/lib/hecks/bluebook/model_check/context_checks.rb +97 -0
- data/lib/hecks/bluebook/model_check/context_map_checks.rb +158 -0
- data/lib/hecks/bluebook/model_check/lifecycle_checks.rb +27 -5
- data/lib/hecks/bluebook/model_check/one_of_checks.rb +106 -0
- data/lib/hecks/bluebook/model_check/orphan_checks.rb +129 -0
- data/lib/hecks/bluebook/model_check/reaction_chains.rb +114 -0
- data/lib/hecks/bluebook/model_check/reaction_cycles.rb +53 -0
- data/lib/hecks/bluebook/model_check/reaction_depth_checks.rb +58 -0
- data/lib/hecks/bluebook/model_check/reaction_walk.rb +62 -0
- data/lib/hecks/bluebook/model_check/redundant_relationships.rb +38 -0
- data/lib/hecks/bluebook/model_check/reference_checks.rb +11 -1
- data/lib/hecks/bluebook/model_check/saga_checks.rb +8 -3
- data/lib/hecks/bluebook/model_check/verbs.rb +4 -4
- data/lib/hecks/bluebook/model_check.rb +33 -35
- data/lib/hecks/bluebook/named_pattern.rb +12 -1
- data/lib/hecks/bluebook/one_of_union.rb +35 -0
- data/lib/hecks/bluebook/pattern_subset.rb +12 -3
- data/lib/hecks/bluebook/process_manager.rb +20 -3
- data/lib/hecks/bluebook/project_loader.rb +3 -2
- data/lib/hecks/bluebook/project_register.rb +21 -5
- data/lib/hecks/bluebook/query.rb +4 -1
- data/lib/hecks/bluebook/read_model.rb +5 -5
- data/lib/hecks/bluebook/reference.rb +3 -3
- data/lib/hecks/bluebook/smoke_test.rb +15 -13
- data/lib/hecks/bluebook/synthesizer.rb +48 -13
- data/lib/hecks/bluebook/translation.rb +5 -3
- data/lib/hecks/bluebook/translation_expression/parser.rb +14 -5
- data/lib/hecks/bluebook/translation_plan.rb +9 -2
- data/lib/hecks/bluebook.rb +6 -3
- data/lib/hecks/bulk/bluebook/adapters/row_sweeper.adapter +7 -0
- data/lib/hecks/bulk/bluebook/bulk.behaviors +18 -0
- data/lib/hecks/bulk/bluebook/bulk.bluebook +126 -0
- data/lib/hecks/bulk/bluebook/bulk.hecksagon +11 -0
- data/lib/hecks/bulk/bluebook/bulk.ports.hecksagon +13 -0
- data/lib/hecks/bulk/plan.rb +60 -0
- data/lib/hecks/bulk/row_sweeper.rb +34 -0
- data/lib/hecks/bulk/set_arguments.rb +68 -0
- data/lib/hecks/bulk/set_eligibility.rb +50 -0
- data/lib/hecks/bulk/set_planner.rb +100 -0
- data/lib/hecks/bulk/set_sweep.rb +78 -0
- data/lib/hecks/bulk/sweep.rb +67 -0
- data/lib/hecks/ci_pipeline/adapters/ci_cache.adapter +7 -0
- data/lib/hecks/ci_pipeline/adapters/ci_cache.rb +39 -0
- data/lib/hecks/ci_pipeline/adapters/ci_runner.adapter +7 -0
- data/lib/hecks/ci_pipeline/adapters/ci_runner.rb +55 -0
- data/lib/hecks/ci_pipeline/ci_pipeline.behaviors +322 -0
- data/lib/hecks/ci_pipeline/ci_pipeline.bluebook +938 -0
- data/lib/hecks/ci_pipeline/ci_pipeline.hecksagon +12 -0
- data/lib/hecks/ci_pipeline/ci_pipeline.ports.hecksagon +16 -0
- data/lib/hecks/ci_speed/adapters/ci_reader.adapter +7 -0
- data/lib/hecks/ci_speed/adapters/ci_reader.rb +141 -0
- data/lib/hecks/ci_speed/adapters/local_runner/answer.rb +41 -0
- data/lib/hecks/ci_speed/adapters/local_runner/catalog.rb +89 -0
- data/lib/hecks/ci_speed/adapters/local_runner/checkouts.rb +89 -0
- data/lib/hecks/ci_speed/adapters/local_runner/launcher.rb +69 -0
- data/lib/hecks/ci_speed/adapters/local_runner/load_average.rb +38 -0
- data/lib/hecks/ci_speed/adapters/local_runner/refs.rb +39 -0
- data/lib/hecks/ci_speed/adapters/local_runner/refused.rb +11 -0
- data/lib/hecks/ci_speed/adapters/local_runner/verdict.rb +71 -0
- data/lib/hecks/ci_speed/adapters/local_runner.adapter +7 -0
- data/lib/hecks/ci_speed/adapters/local_runner.rb +168 -0
- data/lib/hecks/ci_speed/adapters/replayed_runner.rb +39 -0
- data/lib/hecks/ci_speed/ci_speed.behaviors +341 -0
- data/lib/hecks/ci_speed/ci_speed.bluebook +1073 -0
- data/lib/hecks/ci_speed/ci_speed.hecksagon +11 -0
- data/lib/hecks/ci_speed/ci_speed.ports.hecksagon +21 -0
- data/lib/hecks/ci_workflows/ci_workflows.behaviors +13 -0
- data/lib/hecks/ci_workflows/ci_workflows.bluebook +173 -0
- data/lib/hecks/ci_workflows/ci_workflows.hecksagon +15 -0
- data/lib/hecks/codemod.rb +3 -3
- data/lib/hecks/coder/coder.bluebook +385 -0
- data/lib/hecks/coder/coder.hecksagon +12 -0
- data/lib/hecks/command_names/check.rb +39 -0
- data/lib/hecks/command_names/extractor.rb +67 -0
- data/lib/hecks/command_names/index.rb +40 -0
- data/lib/hecks/command_names/resolution.rb +81 -0
- data/lib/hecks/command_names/scan.rb +54 -0
- data/lib/hecks/command_names/sources.rb +73 -0
- data/lib/hecks/command_names.rb +26 -0
- data/lib/hecks/composer/adapters/claude_composer.rb +3 -2
- data/lib/hecks/composer/agent.rb +1 -1
- data/lib/hecks/composer/assistant.rb +2 -2
- data/lib/hecks/composer/behavior_run.rb +7 -1
- data/lib/hecks/composer/bluebook/composer.bluebook +18 -25
- data/lib/hecks/composer/bluebook/composer.hecksagon +1 -1
- data/lib/hecks/composer/board.rb +6 -6
- data/lib/hecks/composer/board_edit/command_text.rb +4 -1
- data/lib/hecks/composer/board_edit/source.rb +18 -9
- data/lib/hecks/composer/board_edit.rb +2 -2
- data/lib/hecks/composer/check.rb +22 -22
- data/lib/hecks/composer/domains.rb +1 -1
- data/lib/hecks/composer/jobs.rb +1 -1
- data/lib/hecks/composer/live.rb +19 -13
- data/lib/hecks/composer/outline.rb +4 -1
- data/lib/hecks/composer/page.html +1 -1
- data/lib/hecks/composer/panels.rb +6 -3
- data/lib/hecks/composer/parse.rb +53 -36
- data/lib/hecks/composer/session.rb +22 -16
- data/lib/hecks/composer/toolbox.rb +17 -7
- data/lib/hecks/composer/web.rb +3 -2
- data/lib/hecks/composer/workbench.rb +12 -9
- data/lib/hecks/composer.rb +1 -1
- data/lib/hecks/construct.rb +7 -7
- data/lib/hecks/corpus/adapters.rb +2 -1
- data/lib/hecks/corpus/generated_key.rb +205 -0
- data/lib/hecks/corpus/listing.rb +34 -0
- data/lib/hecks/corpus/routes.rb +25 -5
- data/lib/hecks/corpus/rust.rb +86 -35
- data/lib/hecks/corpus/sources.rb +14 -12
- data/lib/hecks/corpus/worlds.rb +2 -1
- data/lib/hecks/corpus.rb +57 -54
- data/lib/hecks/deploy/bluebook/deploy.bluebook +304 -518
- data/lib/hecks/deprecation.rb +23 -0
- data/lib/hecks/doc/reference/harvester.rb +4 -1
- data/lib/hecks/doc/reference/readme.rb +11 -6
- data/lib/hecks/doc/reference/rendering.rb +12 -6
- data/lib/hecks/doc/reference.rb +8 -6
- data/lib/hecks/embryonaut_bluebook/lock.rb +5 -1
- data/lib/hecks/embryonaut_bluebook/manifest.rb +8 -2
- data/lib/hecks/embryonaut_bluebook/registry/changelog.rb +4 -1
- data/lib/hecks/embryonaut_bluebook/registry/versions.rb +13 -2
- data/lib/hecks/embryonaut_bluebook/registry.rb +5 -2
- data/lib/hecks/embryonaut_bluebook/shape.rb +1 -1
- data/lib/hecks/embryonaut_bluebook/vendor.rb +5 -2
- data/lib/hecks/embryonaut_bluebook/vendor_cli.rb +15 -1
- data/lib/hecks/embryonaut_bluebook.rb +26 -12
- data/lib/hecks/engine_agreement.rb +4 -1
- data/lib/hecks/fixer/fixer.bluebook +240 -0
- data/lib/hecks/fixer/fixer.hecksagon +17 -0
- data/lib/hecks/forms/app/commands.rb +35 -2
- data/lib/hecks/forms/app/records.rb +12 -12
- data/lib/hecks/forms/app/responses.rb +1 -1
- data/lib/hecks/forms/app.rb +22 -19
- data/lib/hecks/forms/command_form_renderer.rb +2 -2
- data/lib/hecks/forms/field_controls.rb +6 -1
- data/lib/hecks/forms/field_shape.rb +14 -6
- data/lib/hecks/forms/html.rb +8 -7
- data/lib/hecks/forms/html_pattern.rb +80 -0
- data/lib/hecks/forms/index_renderer.rb +11 -11
- data/lib/hecks/forms/params.rb +2 -1
- data/lib/hecks/forms/port_argument.rb +4 -1
- data/lib/hecks/forms/query_form_renderer.rb +1 -1
- data/lib/hecks/forms/record_renderer.rb +2 -2
- data/lib/hecks/forms/record_table.rb +1 -1
- data/lib/hecks/forms/reference_options.rb +2 -1
- data/lib/hecks/forms/text_shape.rb +4 -1
- data/lib/hecks/forms/value_object_shape.rb +1 -1
- data/lib/hecks/forms.rb +7 -7
- data/lib/hecks/fqn.rb +8 -2
- data/lib/hecks/framework/bluebook/change.bluebook +10 -15
- data/lib/hecks/framework/bluebook/change.hecksagon +1 -1
- data/lib/hecks/framework/bluebook/compliance.bluebook +27 -126
- data/lib/hecks/framework/bluebook/console_settings.bluebook +199 -114
- data/lib/hecks/framework/bluebook/framework.hecksagon +7 -0
- data/lib/hecks/framework/bluebook/governance.bluebook +36 -14
- data/lib/hecks/framework/bluebook/identity.bluebook +1 -1
- data/lib/hecks/framework/bluebook/privacy.bluebook +123 -19
- data/lib/hecks/framework/bluebook/privacy.key_keeper.adapter +7 -0
- data/lib/hecks/framework/bluebook/privacy.ports.hecksagon +19 -0
- data/lib/hecks/framework/compliance.oidc.json +6 -36
- data/lib/hecks/framework/console_settings.oidc.json +39 -24
- data/lib/hecks/framework/privacy.oidc.json +25 -0
- data/lib/hecks/framework.rb +24 -8
- data/lib/hecks/framework_bluebooks.rb +181 -0
- data/lib/hecks/freezer.rb +7 -1
- data/lib/hecks/fuzzing/combination_miner.rb +9 -6
- data/lib/hecks/fuzzing/concurrent_dispatch/world_copy.rb +4 -1
- data/lib/hecks/fuzzing/concurrent_dispatch.rb +2 -2
- data/lib/hecks/fuzzing/domain_generator.rb +8 -3
- data/lib/hecks/fuzzing/era_boundary.rb +1 -1
- data/lib/hecks/fuzzing/failure_triage.rb +13 -6
- data/lib/hecks/fuzzing/form_census/{chapter_forms.rb → bluebook_forms.rb} +12 -12
- data/lib/hecks/fuzzing/form_census.rb +14 -12
- data/lib/hecks/fuzzing/generated_domain_check.rb +2 -1
- data/lib/hecks/fuzzing/isolated_boot/postgres_scratch.rb +14 -15
- data/lib/hecks/fuzzing/isolated_boot/scratch_schema.rb +19 -9
- data/lib/hecks/fuzzing/isolated_boot/worlds.rb +36 -13
- data/lib/hecks/fuzzing/isolated_boot.rb +5 -6
- data/lib/hecks/fuzzing/mutation/checks.rb +5 -4
- data/lib/hecks/fuzzing/mutation/finders.rb +25 -8
- data/lib/hecks/fuzzing/mutation/observation.rb +3 -1
- data/lib/hecks/fuzzing/mutation/source_map.rb +3 -1
- data/lib/hecks/fuzzing/nondeterministic.rb +10 -4
- data/lib/hecks/fuzzing/outside_world.rb +1 -1
- data/lib/hecks/fuzzing/projection_agreement/binding.rb +63 -0
- data/lib/hecks/fuzzing/projection_agreement/comparison.rb +104 -0
- data/lib/hecks/fuzzing/projection_agreement.rb +68 -0
- data/lib/hecks/fuzzing/properties/aggregation_recompute.rb +1 -1
- data/lib/hecks/fuzzing/properties/bulk_parity.rb +118 -0
- data/lib/hecks/fuzzing/properties/catalog.rb +7 -0
- data/lib/hecks/fuzzing/properties/cursor_walks.rb +56 -0
- data/lib/hecks/fuzzing/properties/dispatch_and_mutations.rb +39 -1
- data/lib/hecks/fuzzing/properties/group_by_recompute.rb +6 -2
- data/lib/hecks/fuzzing/properties/grouped_reduction_recompute.rb +88 -0
- data/lib/hecks/fuzzing/properties/guards.rb +4 -1
- data/lib/hecks/fuzzing/properties/invariants_and_aggregation.rb +12 -2
- data/lib/hecks/fuzzing/properties/lifecycle_and_replay.rb +2 -1
- data/lib/hecks/fuzzing/properties/mutation_recompute.rb +3 -2
- data/lib/hecks/fuzzing/properties/piece_invariants.rb +3 -2
- data/lib/hecks/fuzzing/properties/query_rows.rb +5 -3
- data/lib/hecks/fuzzing/properties/querying.rb +6 -3
- data/lib/hecks/fuzzing/properties/read_model_names.rb +8 -2
- data/lib/hecks/fuzzing/properties/read_models.rb +2 -1
- data/lib/hecks/fuzzing/properties/refusals.rb +35 -0
- data/lib/hecks/fuzzing/properties/role_checks.rb +3 -2
- data/lib/hecks/fuzzing/properties/set_transform_parity.rb +73 -0
- data/lib/hecks/fuzzing/properties/tenant_scope.rb +3 -2
- data/lib/hecks/fuzzing/properties.rb +6 -0
- data/lib/hecks/fuzzing/qa_settings.rb +4 -4
- data/lib/hecks/fuzzing/replay/fan_out.rb +4 -3
- data/lib/hecks/fuzzing/replay/filters.rb +2 -2
- data/lib/hecks/fuzzing/replay/guard_check.rb +2 -2
- data/lib/hecks/fuzzing/replay/mutation_trace.rb +23 -10
- data/lib/hecks/fuzzing/replay/observation.rb +11 -7
- data/lib/hecks/fuzzing/replay/read_only_steps.rb +40 -3
- data/lib/hecks/fuzzing/replay/refusal_trace.rb +44 -0
- data/lib/hecks/fuzzing/replay/role_check.rb +2 -2
- data/lib/hecks/fuzzing/replay/session.rb +38 -18
- data/lib/hecks/fuzzing/replay/validation_reports.rb +61 -0
- data/lib/hecks/fuzzing/replay.rb +2 -2
- data/lib/hecks/fuzzing/rust_gap_manifest.rb +3 -3
- data/lib/hecks/fuzzing/self_consistency/saga_redelivery.rb +18 -8
- data/lib/hecks/fuzzing/self_consistency/sagas.rb +2 -2
- data/lib/hecks/fuzzing/self_consistency/value_objects.rb +9 -2
- data/lib/hecks/fuzzing/self_consistency.rb +4 -8
- data/lib/hecks/fuzzing/sequence_generator/adversary/argument_mutations.rb +15 -5
- data/lib/hecks/fuzzing/sequence_generator/adversary/caller_draw.rb +14 -10
- data/lib/hecks/fuzzing/sequence_generator/adversary/precedence_mutations.rb +1 -1
- data/lib/hecks/fuzzing/sequence_generator/argument_drawing.rb +2 -1
- data/lib/hecks/fuzzing/sequence_generator/catalog.rb +21 -13
- data/lib/hecks/fuzzing/sequence_generator/picker.rb +1 -1
- data/lib/hecks/fuzzing/sequence_generator/query_binding.rb +3 -3
- data/lib/hecks/fuzzing/sequence_generator/step_builder.rb +2 -2
- data/lib/hecks/fuzzing/sequence_generator.rb +1 -2
- data/lib/hecks/fuzzing/shrinker.rb +4 -1
- data/lib/hecks/fuzzing/value_generator.rb +14 -8
- data/lib/hecks/fuzzing.rb +1 -0
- data/lib/hecks/grammar/dialects.rb +56 -0
- data/lib/hecks/grammar/evolve/arguments.rb +1 -1
- data/lib/hecks/grammar/evolve/keywords.rb +1 -1
- data/lib/hecks/grammar/evolve/seeds.rb +34 -8
- data/lib/hecks/grammar/expression.bluebook +3 -0
- data/lib/hecks/grammar/expression_operators.json +1071 -1
- data/lib/hecks/grammar/operators.rb +45 -22
- data/lib/hecks/grammar/translation.bluebook +3 -1
- data/lib/hecks/grammar/translation_ledger.rb +45 -0
- data/lib/hecks/grammar/translation_rules.json +710 -0
- data/lib/hecks/grammar.rb +27 -23
- data/lib/hecks/hecks/adapter_words.json +7 -0
- data/lib/hecks/hecks/adapters/area_overlap.rb +156 -0
- data/lib/hecks/hecks/adapters/claude_agents.adapter +7 -0
- data/lib/hecks/hecks/adapters/claude_agents.rb +124 -0
- data/lib/hecks/hecks/adapters/claude_transcript.rb +71 -0
- data/lib/hecks/hecks/adapters/codebase/batch_runs.rb +60 -0
- data/lib/hecks/hecks/adapters/codebase/cache_clearing.rb +32 -0
- data/lib/hecks/hecks/adapters/codebase/cargo_tests.rb +50 -0
- data/lib/hecks/hecks/adapters/codebase/changed_specs.rb +52 -0
- data/lib/hecks/hecks/adapters/codebase/codemods.rb +3 -2
- data/lib/hecks/hecks/adapters/codebase/composing.rb +3 -2
- data/lib/hecks/hecks/adapters/codebase/conformance.rb +30 -1
- data/lib/hecks/hecks/adapters/codebase/corpus_queries.rb +53 -0
- data/lib/hecks/hecks/adapters/codebase/corpus_tasks.rb +1 -1
- data/lib/hecks/hecks/adapters/codebase/declarations.rb +144 -0
- data/lib/hecks/hecks/adapters/codebase/generated_keys.rb +31 -0
- data/lib/hecks/hecks/adapters/codebase/impacted_specs.rb +110 -0
- data/lib/hecks/hecks/adapters/codebase/inspection.rb +5 -4
- data/lib/hecks/hecks/adapters/codebase/language.rb +3 -2
- data/lib/hecks/hecks/adapters/codebase/launcher_text.rb +39 -0
- data/lib/hecks/hecks/adapters/codebase/minitest_runs.rb +52 -0
- data/lib/hecks/hecks/adapters/codebase/npm_registry.rb +7 -1
- data/lib/hecks/hecks/adapters/codebase/previewing.rb +62 -0
- data/lib/hecks/hecks/adapters/codebase/promotion/gate.rb +1 -1
- data/lib/hecks/hecks/adapters/codebase/reading.rb +94 -0
- data/lib/hecks/hecks/adapters/codebase/regeneration.rb +57 -10
- data/lib/hecks/hecks/adapters/codebase/release_facts.rb +1 -1
- data/lib/hecks/hecks/adapters/codebase/ruby_child.rb +5 -2
- data/lib/hecks/hecks/adapters/codebase/run_options.rb +35 -0
- data/lib/hecks/hecks/adapters/codebase/scripting.rb +39 -0
- data/lib/hecks/hecks/adapters/codebase/search.rb +91 -0
- data/lib/hecks/hecks/adapters/codebase/source_tree.rb +114 -51
- data/lib/hecks/hecks/adapters/codebase/spec_files/report.rb +71 -0
- data/lib/hecks/hecks/adapters/codebase/spec_files/shared_run.rb +73 -0
- data/lib/hecks/hecks/adapters/codebase/spec_files.rb +95 -0
- data/lib/hecks/hecks/adapters/codebase/static_directory.rb +59 -0
- data/lib/hecks/hecks/adapters/codebase/style.rb +2 -2
- data/lib/hecks/hecks/adapters/codebase/test_runner.rb +82 -14
- data/lib/hecks/hecks/adapters/codebase/test_scope.rb +105 -0
- data/lib/hecks/hecks/adapters/codebase/test_suite.rb +73 -21
- data/lib/hecks/hecks/adapters/console_capture.rb +28 -1
- data/lib/hecks/hecks/adapters/cost_explorer.rb +9 -1
- data/lib/hecks/hecks/adapters/deploy_plans.rb +12 -3
- data/lib/hecks/hecks/adapters/deploy_toolchain/answers.rb +14 -5
- data/lib/hecks/hecks/adapters/deploy_toolchain/scripts.rb +6 -3
- data/lib/hecks/hecks/adapters/deploy_toolchain.rb +3 -3
- data/lib/hecks/hecks/adapters/desktop_alert/alert.rb +50 -0
- data/lib/hecks/hecks/adapters/desktop_alert.adapter +7 -0
- data/lib/hecks/hecks/adapters/desktop_alert.rb +99 -0
- data/lib/hecks/hecks/adapters/dispatching/brief_text.rb +51 -0
- data/lib/hecks/hecks/adapters/dispatching/launcher.rb +97 -0
- data/lib/hecks/hecks/adapters/dispatching/plan.rb +82 -0
- data/lib/hecks/hecks/adapters/dispatching/rows.rb +66 -0
- data/lib/hecks/hecks/adapters/dispatching/settings.rb +81 -0
- data/lib/hecks/hecks/adapters/dispatching/settler.rb +40 -0
- data/lib/hecks/hecks/adapters/dispatching.adapter +7 -0
- data/lib/hecks/hecks/adapters/dispatching.rb +93 -0
- data/lib/hecks/hecks/adapters/doctor_reading/report.rb +27 -0
- data/lib/hecks/hecks/adapters/doctor_reading.adapter +7 -0
- data/lib/hecks/hecks/adapters/doctor_reading.rb +161 -0
- data/lib/hecks/hecks/adapters/finding_github.rb +4 -1
- data/lib/hecks/hecks/adapters/first_admin.rb +16 -9
- data/lib/hecks/hecks/adapters/git.rb +5 -5
- data/lib/hecks/hecks/adapters/git_checkout.adapter +7 -0
- data/lib/hecks/hecks/adapters/git_checkout.rb +99 -0
- data/lib/hecks/hecks/adapters/git_commit.adapter +7 -0
- data/lib/hecks/hecks/adapters/git_commit.rb +61 -0
- data/lib/hecks/hecks/adapters/git_landing/finisher.rb +82 -0
- data/lib/hecks/hecks/adapters/git_landing/foreign_hold.rb +111 -0
- data/lib/hecks/hecks/adapters/git_landing/line.rb +171 -0
- data/lib/hecks/hecks/adapters/git_landing/lineup.rb +98 -0
- data/lib/hecks/hecks/adapters/git_landing/merger.rb +85 -0
- data/lib/hecks/hecks/adapters/git_landing/numbering.rb +54 -0
- data/lib/hecks/hecks/adapters/git_landing/rivals.rb +59 -0
- data/lib/hecks/hecks/adapters/git_landing/ticket.rb +156 -0
- data/lib/hecks/hecks/adapters/git_landing/tidy.rb +147 -0
- data/lib/hecks/hecks/adapters/git_landing/train.rb +104 -0
- data/lib/hecks/hecks/adapters/git_landing/verdict.rb +64 -0
- data/lib/hecks/hecks/adapters/git_landing/waiting.rb +120 -0
- data/lib/hecks/hecks/adapters/git_landing.adapter +7 -0
- data/lib/hecks/hecks/adapters/git_landing.rb +189 -0
- data/lib/hecks/hecks/adapters/git_launcher.adapter +7 -0
- data/lib/hecks/hecks/adapters/git_launcher.rb +82 -0
- data/lib/hecks/hecks/adapters/git_main_pull.rb +99 -0
- data/lib/hecks/hecks/adapters/git_refs.rb +8 -4
- data/lib/hecks/hecks/adapters/git_rehearsal/bench.rb +82 -0
- data/lib/hecks/hecks/adapters/git_rehearsal/due.rb +84 -0
- data/lib/hecks/hecks/adapters/git_rehearsal/impacted/skips.rb +47 -0
- data/lib/hecks/hecks/adapters/git_rehearsal/impacted/spec_run.rb +75 -0
- data/lib/hecks/hecks/adapters/git_rehearsal/impacted.rb +181 -0
- data/lib/hecks/hecks/adapters/git_rehearsal/line.rb +76 -0
- data/lib/hecks/hecks/adapters/git_rehearsal/reading.rb +24 -0
- data/lib/hecks/hecks/adapters/git_rehearsal/record.rb +146 -0
- data/lib/hecks/hecks/adapters/git_rehearsal/spec_map/corpus.rb +25 -0
- data/lib/hecks/hecks/adapters/git_rehearsal/spec_map/needles.rb +61 -0
- data/lib/hecks/hecks/adapters/git_rehearsal/spec_map.rb +105 -0
- data/lib/hecks/hecks/adapters/git_rehearsal/stages.rb +118 -0
- data/lib/hecks/hecks/adapters/git_rehearsal.adapter +7 -0
- data/lib/hecks/hecks/adapters/git_rehearsal.rb +141 -0
- data/lib/hecks/hecks/adapters/git_train/bench.rb +76 -0
- data/lib/hecks/hecks/adapters/git_train/branch_guard.rb +62 -0
- data/lib/hecks/hecks/adapters/git_train/carriage.rb +115 -0
- data/lib/hecks/hecks/adapters/git_train/launcher_shape.rb +75 -0
- data/lib/hecks/hecks/adapters/git_train/local_branch.rb +69 -0
- data/lib/hecks/hecks/adapters/git_train/mint_step.rb +47 -0
- data/lib/hecks/hecks/adapters/git_train/reading.rb +26 -0
- data/lib/hecks/hecks/adapters/git_train/settlement.rb +167 -0
- data/lib/hecks/hecks/adapters/git_train/survey.rb +60 -0
- data/lib/hecks/hecks/adapters/git_train.adapter +7 -0
- data/lib/hecks/hecks/adapters/git_train.rb +146 -0
- data/lib/hecks/hecks/adapters/git_worktree_add.adapter +7 -0
- data/lib/hecks/hecks/adapters/git_worktree_add.rb +68 -0
- data/lib/hecks/hecks/adapters/git_worktrees.rb +81 -0
- data/lib/hecks/hecks/adapters/github_pull_requests.adapter +7 -0
- data/lib/hecks/hecks/adapters/github_pull_requests.rb +109 -0
- data/lib/hecks/hecks/adapters/host_http.rb +9 -8
- data/lib/hecks/hecks/adapters/in_process_access.rb +3 -2
- data/lib/hecks/hecks/adapters/in_process_boot.rb +68 -39
- data/lib/hecks/hecks/adapters/in_process_operations.rb +40 -20
- data/lib/hecks/hecks/adapters/in_process_undo.rb +17 -0
- data/lib/hecks/hecks/adapters/journal_store/changes.rb +18 -9
- data/lib/hecks/hecks/adapters/journal_store/compaction.rb +14 -7
- data/lib/hecks/hecks/adapters/journal_store/edge_audit.rb +35 -20
- data/lib/hecks/hecks/adapters/journal_store/examination.rb +20 -4
- data/lib/hecks/hecks/adapters/journal_store/held_domain/renaming.rb +45 -0
- data/lib/hecks/hecks/adapters/journal_store/held_domain.rb +47 -9
- data/lib/hecks/hecks/adapters/journal_store/held_mint.rb +128 -0
- data/lib/hecks/hecks/adapters/journal_store/readings/attestation.rb +3 -3
- data/lib/hecks/hecks/adapters/journal_store/readings/boot_status.rb +58 -0
- data/lib/hecks/hecks/adapters/journal_store/readings/composition.rb +39 -0
- data/lib/hecks/hecks/adapters/journal_store/readings/event_trace/outcomes.rb +76 -0
- data/lib/hecks/hecks/adapters/journal_store/readings/event_trace/postgres_reader.rb +44 -0
- data/lib/hecks/hecks/adapters/journal_store/readings/event_trace/reader.rb +64 -0
- data/lib/hecks/hecks/adapters/journal_store/readings/event_trace/report.rb +102 -0
- data/lib/hecks/hecks/adapters/journal_store/readings/event_trace/sqlite_reader.rb +43 -0
- data/lib/hecks/hecks/adapters/journal_store/readings/event_trace/stores.rb +80 -0
- data/lib/hecks/hecks/adapters/journal_store/readings/event_trace/subscribers.rb +72 -0
- data/lib/hecks/hecks/adapters/journal_store/readings/event_trace/summary.rb +39 -0
- data/lib/hecks/hecks/adapters/journal_store/readings/event_trace.rb +39 -0
- data/lib/hecks/hecks/adapters/journal_store/readings/mint_plan/command_line.rb +57 -0
- data/lib/hecks/hecks/adapters/journal_store/readings/mint_plan/edge_guards.rb +107 -0
- data/lib/hecks/hecks/adapters/journal_store/readings/mint_plan/inspection.rb +94 -0
- data/lib/hecks/hecks/adapters/journal_store/readings/mint_plan/plan.rb +66 -0
- data/lib/hecks/hecks/adapters/journal_store/readings/mint_plan/report.rb +72 -0
- data/lib/hecks/hecks/adapters/journal_store/readings/mint_plan/store.rb +34 -0
- data/lib/hecks/hecks/adapters/journal_store/readings/mint_plan/watermark_guards.rb +86 -0
- data/lib/hecks/hecks/adapters/journal_store/readings/mint_plan/world.rb +66 -0
- data/lib/hecks/hecks/adapters/journal_store/readings/mint_plan.rb +36 -0
- data/lib/hecks/hecks/adapters/journal_store/readings/projection_audit.rb +64 -0
- data/lib/hecks/hecks/adapters/journal_store/readings/scaffolding.rb +10 -7
- data/lib/hecks/hecks/adapters/journal_store/readings/sqlite_status/database_reading.rb +113 -0
- data/lib/hecks/hecks/adapters/journal_store/readings/sqlite_status/read_only.rb +33 -0
- data/lib/hecks/hecks/adapters/journal_store/readings/sqlite_status.rb +32 -0
- data/lib/hecks/hecks/adapters/journal_store/readings.rb +15 -3
- data/lib/hecks/hecks/adapters/journal_store/reconcile_report.rb +60 -0
- data/lib/hecks/hecks/adapters/journal_store/reconciliation.rb +121 -0
- data/lib/hecks/hecks/adapters/journal_store/translation_write/shape_edge.rb +59 -0
- data/lib/hecks/hecks/adapters/journal_store/translation_write.rb +123 -0
- data/lib/hecks/hecks/adapters/journal_store.rb +18 -2
- data/lib/hecks/hecks/adapters/landed_work.adapter +7 -0
- data/lib/hecks/hecks/adapters/landed_work.rb +52 -0
- data/lib/hecks/hecks/adapters/ledger/chain.rb +63 -0
- data/lib/hecks/hecks/adapters/ledger/journal.rb +105 -0
- data/lib/hecks/hecks/adapters/ledger.adapter +7 -0
- data/lib/hecks/hecks/adapters/ledger.rb +89 -0
- data/lib/hecks/hecks/adapters/local_files.rb +21 -7
- data/lib/hecks/hecks/adapters/pg_admin.rb +7 -1
- data/lib/hecks/hecks/adapters/process_pool.rb +3 -2
- data/lib/hecks/hecks/adapters/push_size.rb +57 -0
- data/lib/hecks/hecks/adapters/qa_quiescing.adapter +7 -0
- data/lib/hecks/hecks/adapters/qa_quiescing.rb +57 -0
- data/lib/hecks/hecks/adapters/rails_app_files.rb +64 -0
- data/lib/hecks/hecks/adapters/rehearsal/audit_report.rb +4 -1
- data/lib/hecks/hecks/adapters/rehearsal/inquiry.rb +39 -12
- data/lib/hecks/hecks/adapters/rehearsal/memory_store.rb +65 -5
- data/lib/hecks/hecks/adapters/rehearsal/postgres_census.rb +4 -4
- data/lib/hecks/hecks/adapters/rehearsal/postgres_reading.rb +30 -7
- data/lib/hecks/hecks/adapters/rehearsal/postgres_store.rb +4 -1
- data/lib/hecks/hecks/adapters/rehearsal/rust_audit.rb +1 -1
- data/lib/hecks/hecks/adapters/rehearsal/subject.rb +44 -9
- data/lib/hecks/hecks/adapters/rust_toolchain.rb +15 -4
- data/lib/hecks/hecks/adapters/rust_workspace.rb +1 -1
- data/lib/hecks/hecks/adapters/settings_reading/disposable_ledger.rb +32 -0
- data/lib/hecks/hecks/adapters/settings_reading/push_nudge.rb +26 -0
- data/lib/hecks/hecks/adapters/settings_reading/stores.rb +76 -0
- data/lib/hecks/hecks/adapters/settings_reading/test_scope.rb +28 -0
- data/lib/hecks/hecks/adapters/settings_reading.adapter +7 -0
- data/lib/hecks/hecks/adapters/settings_reading.rb +168 -0
- data/lib/hecks/hecks/adapters/shell.rb +3 -0
- data/lib/hecks/hecks/adapters/site_toolchain.rb +8 -8
- data/lib/hecks/hecks/adapters/started_work.rb +42 -0
- data/lib/hecks/hecks/adapters/terminal.rb +12 -10
- data/lib/hecks/hecks/adapters/where_reading.adapter +7 -0
- data/lib/hecks/hecks/adapters/where_reading.rb +64 -0
- data/lib/hecks/hecks/adapters/work_areas.rb +57 -0
- data/lib/hecks/hecks/adapters/work_limits.rb +57 -0
- data/lib/hecks/hecks/adapters/world_clearing.rb +2 -1
- data/lib/hecks/hecks/build/build.bluebook +576 -0
- data/lib/hecks/hecks/check/check.bluebook +966 -0
- data/lib/hecks/hecks/codebase.behaviors +72 -4
- data/lib/hecks/hecks/composer.behaviors +10 -10
- data/lib/hecks/hecks/context_map.hecksagon +7 -6
- data/lib/hecks/hecks/custodian.behaviors +124 -3
- data/lib/hecks/hecks/delegate/delegate.bluebook +355 -0
- data/lib/hecks/hecks/deliver/deliver.bluebook +945 -0
- data/lib/hecks/hecks/deploy.behaviors +1 -1
- data/lib/hecks/hecks/deploy.world +3 -4
- data/lib/hecks/hecks/evolve/evolve.bluebook +788 -0
- data/lib/hecks/hecks/find/find.bluebook +81 -0
- data/lib/hecks/hecks/hecks.behaviors +2 -2
- data/lib/hecks/hecks/hecks.bluebook +0 -138
- data/lib/hecks/hecks/hecks.contextmap +27 -0
- data/lib/hecks/hecks/hecks.hecksagon +312 -23
- data/lib/hecks/hecks/hecks.world +54 -27
- data/lib/hecks/hecks/implicit_moves.json +24 -0
- data/lib/hecks/hecks/migrate/migrate.bluebook +1066 -0
- data/lib/hecks/hecks/operate/operate.bluebook +695 -0
- data/lib/hecks/hecks/quiesce/quiesce.bluebook +117 -0
- data/lib/hecks/hecks/rehearse/rehearse.bluebook +133 -0
- data/lib/hecks/hecks/release/release.bluebook +916 -0
- data/lib/hecks/hecks/serve/serve.bluebook +134 -0
- data/lib/hecks/hecks/shapes.json +3 -0
- data/lib/hecks/hecks/site.behaviors +1 -1
- data/lib/hecks/hecks/site.world +3 -3
- data/lib/hecks/hecks/start/start.bluebook +194 -0
- data/lib/hecks/hecks/test/test.bluebook +638 -0
- data/lib/hecks/hecks/tickets.world +14 -0
- data/lib/hecks/ir.rb +8 -1
- data/lib/hecks/kernel_capabilities/templates.rb +4 -3
- data/lib/hecks/kernel_capabilities.rb +6 -6
- data/lib/hecks/language/adapter.bluebook +61 -0
- data/lib/hecks/language/bluebook/aggregate.bluebook +58 -6
- data/lib/hecks/language/bluebook/attaches/paging.bluebook +1 -1
- data/lib/hecks/language/bluebook/bluebook.behaviors +6 -6
- data/lib/hecks/language/bluebook/bluebook.bluebook +62 -51
- data/lib/hecks/language/bluebook/bluebook.hecksagon +1 -1
- data/lib/hecks/language/bluebook/command.bluebook +69 -2
- data/lib/hecks/language/bluebook/entity.bluebook +2 -2
- data/lib/hecks/language/bluebook/policy.behaviors +2 -2
- data/lib/hecks/language/bluebook/policy.bluebook +3 -3
- data/lib/hecks/language/bluebook/process_manager.bluebook +17 -4
- data/lib/hecks/language/bluebook/projection.bluebook +11 -10
- data/lib/hecks/language/bluebook/query.bluebook +1 -0
- data/lib/hecks/language/bluebook/shape.bluebook +1 -1
- data/lib/hecks/language/bluebook/syntax.bluebook +3 -1
- data/lib/hecks/language/bluebook/vocabulary.bluebook +417 -16
- data/lib/hecks/language/context_map/context_map.bluebook +90 -0
- data/lib/hecks/language/hecksagon/domain_port.bluebook +3 -2
- data/lib/hecks/language/hecksagon/hecksagon.bluebook +8 -8
- data/lib/hecks/language/hecksagon/port_operation.bluebook +14 -0
- data/lib/hecks/language/kinds/run.kind +120 -0
- data/lib/hecks/language/oidc.json +20 -0
- data/lib/hecks/language/port.bluebook +1 -1
- data/lib/hecks/language/sentence/sentence.bluebook +210 -0
- data/lib/hecks/language/translation/translation_aggregate.bluebook +24 -0
- data/lib/hecks/language/world/world.bluebook +6 -6
- data/lib/hecks/literal.rb +8 -2
- data/lib/hecks/module_folders.rb +31 -0
- data/lib/hecks/naming.rb +44 -14
- data/lib/hecks/persistence_legacy_fixture/postgres_stores.rb +7 -6
- data/lib/hecks/persistence_legacy_fixture/regenerate.rb +6 -2
- data/lib/hecks/ports/access_control.rb +2 -2
- data/lib/hecks/ports/agent/answers.rb +2 -2
- data/lib/hecks/ports/agent.rb +2 -2
- data/lib/hecks/ports/authentication.port +6 -0
- data/lib/hecks/ports/authentication.rb +67 -0
- data/lib/hecks/ports/authorization.rb +3 -3
- data/lib/hecks/ports/dispatch.port +5 -0
- data/lib/hecks/ports/dispatch.rb +28 -0
- data/lib/hecks/ports/key_vault.rb +10 -7
- data/lib/hecks/ports/loading.rb +23 -7
- data/lib/hecks/ports/persistence/append_only.rb +44 -9
- data/lib/hecks/ports/persistence/binding_policy.rb +73 -12
- data/lib/hecks/ports/persistence/codec_boundary.rb +4 -3
- data/lib/hecks/ports/persistence/mint_door.rb +74 -0
- data/lib/hecks/ports/persistence/plugins/era/era_check.rb +16 -15
- data/lib/hecks/ports/persistence/plugins/era/era_guard/shape_diff.rb +9 -6
- data/lib/hecks/ports/persistence/plugins/era/era_guard.rb +11 -11
- data/lib/hecks/ports/persistence/plugins/era/era_status.rb +48 -0
- data/lib/hecks/ports/persistence/plugins/era/era_tamper.rb +1 -1
- data/lib/hecks/ports/persistence/plugins/era/projected_bluebook.rb +127 -0
- data/lib/hecks/ports/persistence/plugins/era/storage_shape.rb +6 -6
- data/lib/hecks/ports/persistence/plugins/era/translation/additive_shape.rb +107 -0
- data/lib/hecks/ports/persistence/plugins/era/translation/audit.rb +9 -3
- data/lib/hecks/ports/persistence/plugins/era/translation/compose.rb +155 -0
- data/lib/hecks/ports/persistence/plugins/era/translation/compose_render.rb +79 -0
- data/lib/hecks/ports/persistence/plugins/era/translation/edge_files.rb +49 -0
- data/lib/hecks/ports/persistence/plugins/era/translation/edge_route.rb +61 -0
- data/lib/hecks/ports/persistence/plugins/era/translation/implicit_edge.rb +67 -0
- data/lib/hecks/ports/persistence/plugins/era/translation/reattest.rb +1 -1
- data/lib/hecks/ports/persistence/plugins/era/translation/reconcile.rb +130 -0
- data/lib/hecks/ports/persistence/plugins/era/translation/reconcile_files.rb +35 -0
- data/lib/hecks/ports/persistence/plugins/era/translation/rule_compiler.rb +32 -19
- data/lib/hecks/ports/persistence/plugins/era/translation/scaffold/differ.rb +13 -5
- data/lib/hecks/ports/persistence/plugins/era/translation.rb +5 -0
- data/lib/hecks/ports/persistence/plugins/era.rb +1 -1
- data/lib/hecks/ports/persistence/remote_runtime.rb +3 -2
- data/lib/hecks/ports/persistence/repository_factory.rb +11 -3
- data/lib/hecks/ports/persistence/set_statement.rb +29 -0
- data/lib/hecks/ports/persistence/set_transforming.rb +30 -0
- data/lib/hecks/ports/persistence/state_codec.rb +3 -3
- data/lib/hecks/ports/persistence/stored_name_guard.rb +36 -0
- data/lib/hecks/ports/persistence.rb +2 -4
- data/lib/hecks/ports/projection.rb +4 -1
- data/lib/hecks/ports/query/cursor.rb +131 -0
- data/lib/hecks/ports/query/in_memory.rb +17 -10
- data/lib/hecks/ports/query.rb +4 -2
- data/lib/hecks/ports.rb +1 -0
- data/lib/hecks/projection_files/expression_tables.rb +3 -3
- data/lib/hecks/projection_files/sentence_table.rb +17 -0
- data/lib/hecks/projection_files.rb +16 -13
- data/lib/hecks/projections/bootstrap_table.rb +3 -3
- data/lib/hecks/projections/ci_pipeline/table/checks.rb +137 -0
- data/lib/hecks/projections/ci_pipeline/table/lookup.rb +59 -0
- data/lib/hecks/projections/ci_pipeline/table/refusals.rb +58 -0
- data/lib/hecks/projections/ci_pipeline/table/rows.rb +119 -0
- data/lib/hecks/projections/ci_pipeline/table/tree.rb +30 -0
- data/lib/hecks/projections/ci_pipeline/table/typing.rb +92 -0
- data/lib/hecks/projections/ci_pipeline/table.rb +112 -0
- data/lib/hecks/projections/ci_pipeline/workflows.rb +162 -0
- data/lib/hecks/projections/ci_pipeline/yaml_text.rb +104 -0
- data/lib/hecks/projections/command_schema/shape.rb +95 -0
- data/lib/hecks/projections/command_schema.rb +74 -0
- data/lib/hecks/projections/context_map_diagram.rb +135 -0
- data/lib/hecks/projections/context_relationships.rb +125 -0
- data/lib/hecks/projections/deploy/box/access.rb +7 -4
- data/lib/hecks/projections/deploy/box/caddy.rb +2 -1
- data/lib/hecks/projections/deploy/box/compose.rb +4 -8
- data/lib/hecks/projections/deploy/box/edge.rb +2 -1
- data/lib/hecks/projections/deploy/box/hosting.rb +5 -3
- data/lib/hecks/projections/deploy/box/hosting_parts.rb +27 -12
- data/lib/hecks/projections/deploy/box/logging.rb +2 -3
- data/lib/hecks/projections/deploy/box/roll_recipe.rb +2 -4
- data/lib/hecks/projections/deploy/box/roll_scripts.rb +2 -17
- data/lib/hecks/projections/deploy/box/settings/containers.rb +29 -15
- data/lib/hecks/projections/deploy/box/settings/patterns.rb +5 -6
- data/lib/hecks/projections/deploy/box/settings/types.rb +5 -10
- data/lib/hecks/projections/deploy/box/settings.rb +9 -8
- data/lib/hecks/projections/deploy/box/stack_yaml.rb +10 -14
- data/lib/hecks/projections/deploy/box/templates/box.yaml.tmpl +0 -1
- data/lib/hecks/projections/deploy/box/templates/deploy-service.sh.tmpl +16 -3
- data/lib/hecks/projections/deploy/box/templates/hosting.mk.tmpl +7 -6
- data/lib/hecks/projections/deploy/box/templates/render-compose-taskdef.sh.tmpl +2 -6
- data/lib/hecks/projections/deploy/box/templates/render-compose.sh.tmpl +2 -6
- data/lib/hecks/projections/deploy/box.rb +17 -10
- data/lib/hecks/projections/deploy/fargate/assembly.rb +7 -2
- data/lib/hecks/projections/deploy/fargate/cdn/options.rb +4 -1
- data/lib/hecks/projections/deploy/fargate/check.rb +3 -1
- data/lib/hecks/projections/deploy/fargate/containers/rendering.rb +8 -5
- data/lib/hecks/projections/deploy/fargate/containers/routing.rb +2 -1
- data/lib/hecks/projections/deploy/fargate/monitoring/reading.rb +4 -1
- data/lib/hecks/projections/deploy/fargate/sections.rb +7 -4
- data/lib/hecks/projections/deploy/fargate/settings/ids.rb +4 -1
- data/lib/hecks/projections/deploy/fargate/settings/tuning.rb +13 -4
- data/lib/hecks/projections/deploy/fargate/stack/blocks.rb +6 -1
- data/lib/hecks/projections/deploy/fargate/stack/readers.rb +2 -1
- data/lib/hecks/projections/deploy/fargate/stack.rb +11 -6
- data/lib/hecks/projections/deploy/fargate/yaml.rb +10 -3
- data/lib/hecks/projections/deploy/fargate.rb +8 -3
- data/lib/hecks/projections/deploy/lambda/stack/blocks.rb +4 -2
- data/lib/hecks/projections/deploy/lambda/stack/deploy_chain.rb +2 -1
- data/lib/hecks/projections/deploy/lambda/stack/reading.rb +12 -9
- data/lib/hecks/projections/deploy/lambda/stack/reading_web.rb +7 -2
- data/lib/hecks/projections/deploy/lambda/stack/web.rb +2 -1
- data/lib/hecks/projections/deploy/lambda.rb +8 -3
- data/lib/hecks/projections/deploy/preview/containers.rb +14 -1
- data/lib/hecks/projections/deploy/preview/preview.sh.erb +2 -2
- data/lib/hecks/projections/deploy/preview/routing_yaml.rb +8 -6
- data/lib/hecks/projections/deploy/preview/settings.rb +23 -5
- data/lib/hecks/projections/deploy/preview/task_yaml.rb +1 -1
- data/lib/hecks/projections/deploy/preview/yaml_text.rb +2 -1
- data/lib/hecks/projections/deploy/scripts/settings.rb +5 -2
- data/lib/hecks/projections/deploy/shared.rb +12 -1
- data/lib/hecks/projections/deploy/shared_database_instance.rb +1 -1
- data/lib/hecks/projections/deploy/smoke.rb +3 -1
- data/lib/hecks/projections/deploy/template_diff/normalizer.rb +4 -1
- data/lib/hecks/projections/deploy/templates/fargate/Makefile.tmpl +1 -1
- data/lib/hecks/projections/deploy/templates/lambda/Makefile.tmpl +3 -3
- data/lib/hecks/projections/deploy/templates/lambda/mint_era_shared.tmpl +2 -2
- data/lib/hecks/projections/deploy/templates/lambda/nat_comment_oauth.tmpl +1 -1
- data/lib/hecks/projections/deploy/vercel.rb +1 -1
- data/lib/hecks/projections/diagrams/dispatch.rb +5 -3
- data/lib/hecks/projections/diagrams/statecharts.rb +8 -3
- data/lib/hecks/projections/diagrams/structure.rb +9 -6
- data/lib/hecks/projections/diagrams.rb +9 -7
- data/lib/hecks/projections/glossary/entries.rb +4 -4
- data/lib/hecks/projections/glossary/html/page.rb +4 -1
- data/lib/hecks/projections/glossary/html/parser.rb +13 -4
- data/lib/hecks/projections/glossary/html/sections.rb +4 -1
- data/lib/hecks/projections/glossary/html.rb +10 -3
- data/lib/hecks/projections/glossary/markdown.rb +4 -3
- data/lib/hecks/projections/glossary/mermaid.rb +8 -5
- data/lib/hecks/projections/glossary/sections.rb +1 -1
- data/lib/hecks/projections/glossary/sensitivity.rb +11 -4
- data/lib/hecks/projections/glossary/sentences/actions.rb +5 -2
- data/lib/hecks/projections/glossary/sentences/queries.rb +2 -1
- data/lib/hecks/projections/glossary/sentences/value_objects.rb +15 -6
- data/lib/hecks/projections/glossary/sentences.rb +6 -3
- data/lib/hecks/projections/glossary/slugs.rb +8 -1
- data/lib/hecks/projections/glossary.rb +3 -3
- data/lib/hecks/projections/model.rb +10 -6
- data/lib/hecks/projections/oidc.rb +19 -7
- data/lib/hecks/projections/parser_table.rb +10 -10
- data/lib/hecks/projections/phrase_table.rb +105 -0
- data/lib/hecks/projections/plain_english.rb +14 -4
- data/lib/hecks/projections/rails_app.rb +77 -0
- data/lib/hecks/projections/reference.rb +2 -2
- data/lib/hecks/projections/rust_vocabulary/arguments.rb +1 -1
- data/lib/hecks/projections/rust_vocabulary/tables.rb +20 -0
- data/lib/hecks/projections/rust_vocabulary.rb +14 -17
- data/lib/hecks/projections/site/admin.rb +7 -7
- data/lib/hecks/projections/site/admin_cms.rb +1 -1
- data/lib/hecks/projections/site/cms_editor/blocks.rb +5 -2
- data/lib/hecks/projections/site/cms_editor/checks.rb +9 -6
- data/lib/hecks/projections/site/cms_editor/color.rb +4 -1
- data/lib/hecks/projections/site/cms_editor/composition.rb +20 -18
- data/lib/hecks/projections/site/cms_editor/destructive.rb +4 -1
- data/lib/hecks/projections/site/cms_editor/drafts.rb +6 -2
- data/lib/hecks/projections/site/cms_editor/key_kinds.rb +4 -1
- data/lib/hecks/projections/site/cms_editor/limits.rb +1 -1
- data/lib/hecks/projections/site/cms_editor/media.rb +1 -1
- data/lib/hecks/projections/site/cms_editor/moment.rb +3 -1
- data/lib/hecks/projections/site/cms_editor/pickers.rb +1 -1
- data/lib/hecks/projections/site/cms_editor/preview.rb +10 -4
- data/lib/hecks/projections/site/cms_editor/relations.rb +3 -2
- data/lib/hecks/projections/site/cms_editor/scale_checks.rb +32 -25
- data/lib/hecks/projections/site/cms_editor/scheduling.rb +1 -1
- data/lib/hecks/projections/site/cms_editor/schema.rb +31 -29
- data/lib/hecks/projections/site/cms_editor/setting.rb +34 -27
- data/lib/hecks/projections/site/cms_editor/targets.rb +9 -6
- data/lib/hecks/projections/site/cms_editor/templates/src/app.ts.tmpl +3 -3
- data/lib/hecks/projections/site/cms_editor/templates/src/commands.ts.tmpl +2 -2
- data/lib/hecks/projections/site/cms_editor/templates/src/host.ts.tmpl +1 -1
- data/lib/hecks/projections/site/cms_editor/templates/src/media/handler.ts.tmpl +4 -4
- data/lib/hecks/projections/site/cms_editor/templates/src/names.ts.tmpl +14 -14
- data/lib/hecks/projections/site/cms_editor/templates/src/schema.ts.tmpl +12 -12
- data/lib/hecks/projections/site/cms_editor/templates/src/ui/pages.ts.tmpl +12 -12
- data/lib/hecks/projections/site/cms_editor/templates/src/ui/pickers.ts.tmpl +1 -1
- data/lib/hecks/projections/site/cms_editor/templates/src/ui/schedule.ts.tmpl +2 -2
- data/lib/hecks/projections/site/cms_editor/templates/src/ui/shell.ts.tmpl +11 -11
- data/lib/hecks/projections/site/cms_editor/templates/src/ui/world.ts.tmpl +3 -3
- data/lib/hecks/projections/site/cms_editor/theme_checks.rb +8 -2
- data/lib/hecks/projections/site/cms_editor.rb +19 -16
- data/lib/hecks/projections/site/edge/checks.rb +1 -1
- data/lib/hecks/projections/site/edge/function_checks.rb +1 -1
- data/lib/hecks/projections/site/edge/function_code.rb +4 -1
- data/lib/hecks/projections/site/edge/pattern.rb +4 -1
- data/lib/hecks/projections/site/edge/route_checks.rb +2 -2
- data/lib/hecks/projections/site/edge/rules.rb +1 -1
- data/lib/hecks/projections/site/edge.rb +14 -8
- data/lib/hecks/projections/site/live_edge.rb +2 -2
- data/lib/hecks/projections/site/payload/fields/definitions.rb +1 -1
- data/lib/hecks/projections/site/payload/lifecycle/text.rb +1 -1
- data/lib/hecks/projections/site/payload/model.rb +16 -12
- data/lib/hecks/projections/site/payload/specs.rb +4 -2
- data/lib/hecks/projections/site/payload_driver.rb +9 -8
- data/lib/hecks/projections/site/probe.rb +12 -3
- data/lib/hecks/projections/site/regions.rb +9 -1
- data/lib/hecks/projections/site/role_probe.rb +7 -7
- data/lib/hecks/projections/site/root_rows.rb +4 -4
- data/lib/hecks/projections/site/routes_ts/listings.rb +13 -6
- data/lib/hecks/projections/site/routes_ts.rb +6 -3
- data/lib/hecks/projections/site/site_admin.rb +1 -1
- data/lib/hecks/projections/site/site_cdn.rb +3 -2
- data/lib/hecks/projections/site/site_host.rb +1 -1
- data/lib/hecks/projections/site/site_root.rb +4 -4
- data/lib/hecks/projections/site/table/checks.rb +4 -1
- data/lib/hecks/projections/site/table/navigation_checks.rb +2 -2
- data/lib/hecks/projections/site/table/row_builder.rb +7 -7
- data/lib/hecks/projections/site/table/row_defaults.rb +1 -1
- data/lib/hecks/projections/site/table.rb +32 -26
- data/lib/hecks/projections/statements/actions.rb +4 -1
- data/lib/hecks/projections/statements/relationships.rb +15 -2
- data/lib/hecks/projections/statements.rb +2 -2
- data/lib/hecks/projections/undo.rb +46 -0
- data/lib/hecks/projections/vocabulary.rb +6 -6
- data/lib/hecks/projections/world_skeleton.rb +68 -0
- data/lib/hecks/projections.rb +7 -0
- data/lib/hecks/projector/cli_audience.rb +12 -9
- data/lib/hecks/projector/cli_projector/claims.rb +19 -8
- data/lib/hecks/projector/cli_projector/command_help.rb +6 -1
- data/lib/hecks/projector/cli_projector/command_specs.rb +20 -6
- data/lib/hecks/projector/cli_projector/completion.rb +87 -0
- data/lib/hecks/projector/cli_projector/everyday.rb +40 -0
- data/lib/hecks/projector/cli_projector/json_listing.rb +77 -0
- data/lib/hecks/projector/cli_projector/listing.rb +20 -8
- data/lib/hecks/projector/cli_projector/modules.rb +39 -0
- data/lib/hecks/projector/cli_projector/option_specs.rb +1 -1
- data/lib/hecks/projector/cli_projector/query_specs.rb +2 -1
- data/lib/hecks/projector/cli_projector/renames.rb +58 -0
- data/lib/hecks/projector/cli_projector/search.rb +74 -0
- data/lib/hecks/projector/cli_projector/usage.rb +33 -12
- data/lib/hecks/projector/cli_projector.rb +26 -3
- data/lib/hecks/projector/docs_projector/behaviour.rb +5 -4
- data/lib/hecks/projector/docs_projector/closing.rb +3 -3
- data/lib/hecks/projector/docs_projector.rb +12 -7
- data/lib/hecks/projector/exporter/adapters.rb +15 -2
- data/lib/hecks/projector/exporter/bindings.rb +39 -134
- data/lib/hecks/projector/exporter/plugins.rb +55 -0
- data/lib/hecks/projector/exporter.rb +2 -0
- data/lib/hecks/projector/narrate_projector/commands.rb +4 -3
- data/lib/hecks/projector/narrate_projector/reactions.rb +2 -2
- data/lib/hecks/projector/narrate_projector.rb +9 -8
- data/lib/hecks/projector/target.rb +8 -8
- data/lib/hecks/projector.rb +9 -9
- data/lib/hecks/quality_control/adapters/agent.rb +2 -1
- data/lib/hecks/quality_control/adapters/ci_head.adapter +8 -0
- data/lib/hecks/quality_control/adapters/github_checks.adapter +1 -1
- data/lib/hecks/quality_control/adapters/github_issues.adapter +1 -1
- data/lib/hecks/quality_control/adapters/github_issues.rb +6 -2
- data/lib/hecks/quality_control/adapters/image_roll.adapter +9 -0
- data/lib/hecks/quality_control/adapters/image_roll.rb +158 -0
- data/lib/hecks/quality_control/adapters/main_maintenance.adapter +8 -0
- data/lib/hecks/quality_control/adapters/main_maintenance.rb +275 -0
- data/lib/hecks/quality_control/adapters/pump_tools.adapter +7 -0
- data/lib/hecks/quality_control/adapters/pump_tools.rb +18 -0
- data/lib/hecks/quality_control/adapters/push_due_reader.adapter +8 -0
- data/lib/hecks/quality_control/adapters/push_due_reader.rb +99 -0
- data/lib/hecks/quality_control/adapters/qa_auto_mint.rb +183 -0
- data/lib/hecks/quality_control/adapters/qa_era_mint.rb +94 -0
- data/lib/hecks/quality_control/adapters/qa_quiesce_marker.rb +126 -0
- data/lib/hecks/quality_control/adapters/run_keys.rb +36 -0
- data/lib/hecks/quality_control/adapters/run_reader.adapter +7 -0
- data/lib/hecks/quality_control/adapters/run_reader.rb +188 -0
- data/lib/hecks/quality_control/adapters/target_tools.rb +9 -3
- data/lib/hecks/quality_control/cli/child.rb +2 -0
- data/lib/hecks/quality_control/cli/qa_discover_external_domains/scanning.rb +1 -1
- data/lib/hecks/quality_control/cli/qa_domain_novelty/verdict.rb +1 -1
- data/lib/hecks/quality_control/cli/qa_domain_novelty.rb +3 -3
- data/lib/hecks/quality_control/cli/qa_event_pump/attached.rb +67 -0
- data/lib/hecks/quality_control/cli/qa_event_pump/consumer.rb +103 -0
- data/lib/hecks/quality_control/cli/qa_event_pump/cursors.rb +60 -0
- data/lib/hecks/quality_control/cli/qa_event_pump/reactions.rb +46 -0
- data/lib/hecks/quality_control/cli/qa_event_pump/reader.rb +30 -0
- data/lib/hecks/quality_control/cli/qa_event_pump.rb +76 -0
- data/lib/hecks/quality_control/cli/qa_fix_main_bug/publishing.rb +79 -0
- data/lib/hecks/quality_control/cli/qa_fix_main_bug.rb +167 -0
- data/lib/hecks/quality_control/cli/qa_generated_domains/evaluation.rb +13 -3
- data/lib/hecks/quality_control/cli/qa_generated_domains/promotion.rb +7 -4
- data/lib/hecks/quality_control/cli/qa_generated_domains.rb +4 -1
- data/lib/hecks/quality_control/cli/qa_log_bug/proof.rb +16 -3
- data/lib/hecks/quality_control/cli/qa_main_gate/gate_run.rb +52 -0
- data/lib/hecks/quality_control/cli/qa_main_gate/logging.rb +70 -0
- data/lib/hecks/quality_control/cli/qa_main_gate/shell.rb +47 -0
- data/lib/hecks/quality_control/cli/qa_main_gate/sweeping.rb +59 -0
- data/lib/hecks/quality_control/cli/qa_main_gate/worktree.rb +62 -0
- data/lib/hecks/quality_control/cli/qa_main_gate.rb +135 -0
- data/lib/hecks/quality_control/cli/qa_mine_combinations/booting.rb +4 -2
- data/lib/hecks/quality_control/cli/qa_mutate.rb +161 -0
- data/lib/hecks/quality_control/cli/qa_postgres_role.rb +1 -1
- data/lib/hecks/quality_control/cli/qa_pr_check/checking.rb +2 -3
- data/lib/hecks/quality_control/cli/qa_push_nudge.rb +48 -0
- data/lib/hecks/quality_control/cli/qa_seed_angles/seed.rb +1 -1
- data/lib/hecks/quality_control/cli/qa_sweep/all_mode/report.rb +4 -1
- data/lib/hecks/quality_control/cli/qa_sweep/parsing.rb +2 -1
- data/lib/hecks/quality_control/cli/qa_sweep/seat.rb +1 -1
- data/lib/hecks/quality_control/cli/qa_sweep.rb +4 -1
- data/lib/hecks/quality_control/cli/qa_tick/event_pump_step.rb +34 -0
- data/lib/hecks/quality_control/cli/qa_tick/output.rb +43 -26
- data/lib/hecks/quality_control/cli/qa_tick/preflight.rb +12 -3
- data/lib/hecks/quality_control/cli/qa_tick/push_nudge_step.rb +28 -0
- data/lib/hecks/quality_control/cli/qa_tick/quiesce_guard.rb +51 -0
- data/lib/hecks/quality_control/cli/qa_tick.rb +52 -8
- data/lib/hecks/quality_control/quality_control.behaviors +65 -0
- data/lib/hecks/quality_control/quality_control.bluebook +1224 -103
- data/lib/hecks/quality_control/quality_control.hecksagon +14 -3
- data/lib/hecks/quality_control/quality_control.ports.hecksagon +141 -2
- data/lib/hecks/query_ir/rules.rb +9 -9
- data/lib/hecks/query_ir/touchpoints.rb +10 -9
- data/lib/hecks/query_ir.rb +3 -3
- data/lib/hecks/query_specification/common/comparison.rb +1 -1
- data/lib/hecks/query_specification/common/dsl.rb +3 -4
- data/lib/hecks/query_specification/hop_path.rb +7 -4
- data/lib/hecks/rails_app/bluebook/rails_app.bluebook +60 -0
- data/lib/hecks/rails_app/bluebook/rails_app.hecksagon +4 -0
- data/lib/hecks/refused.rb +125 -0
- data/lib/hecks/release/approval.rb +15 -5
- data/lib/hecks/release/conditions.rb +46 -0
- data/lib/hecks/release/gem_pin.rb +7 -3
- data/lib/hecks/release/lane.rb +1 -1
- data/lib/hecks/release/runner/clean_tree.rb +4 -1
- data/lib/hecks/release/runner/console.rb +2 -1
- data/lib/hecks/release/runner/gem_publisher.rb +4 -4
- data/lib/hecks/release/version_commit.rb +48 -0
- data/lib/hecks/router/aggregate_reader.rb +3 -4
- data/lib/hecks/router/namespace_installer.rb +31 -17
- data/lib/hecks/router.rb +2 -10
- data/lib/hecks/runtime/adapter_lookup.rb +30 -7
- data/lib/hecks/runtime/adapter_namespace.rb +21 -0
- data/lib/hecks/runtime/asking.rb +30 -0
- data/lib/hecks/runtime/caller.rb +22 -0
- data/lib/hecks/runtime/capability_graph.rb +2 -1
- data/lib/hecks/runtime/collecting.rb +86 -0
- data/lib/hecks/runtime/command_interpreter/argument_gate.rb +13 -8
- data/lib/hecks/runtime/command_interpreter/delegation.rb +46 -17
- data/lib/hecks/runtime/command_interpreter/hydration.rb +22 -25
- data/lib/hecks/runtime/command_interpreter/list_mutations.rb +2 -1
- data/lib/hecks/runtime/command_interpreter/persisting.rb +23 -14
- data/lib/hecks/runtime/command_interpreter.rb +19 -12
- data/lib/hecks/runtime/command_rules/admissibility.rb +17 -12
- data/lib/hecks/runtime/command_rules/authorization.rb +8 -6
- data/lib/hecks/runtime/command_rules/emission.rb +0 -1
- data/lib/hecks/runtime/command_rules/guard_state.rb +10 -10
- data/lib/hecks/runtime/command_rules/invariants.rb +3 -1
- data/lib/hecks/runtime/command_rules/missing_reference.rb +25 -0
- data/lib/hecks/runtime/command_rules/one_of_references.rb +44 -0
- data/lib/hecks/runtime/command_rules/references.rb +14 -14
- data/lib/hecks/runtime/command_rules.rb +2 -0
- data/lib/hecks/runtime/declarations.rb +66 -0
- data/lib/hecks/runtime/dependency_planning/analyzer.rb +2 -1
- data/lib/hecks/runtime/dependency_planning/expression_reads.rb +3 -3
- data/lib/hecks/runtime/dependency_planning/mutation_analysis.rb +2 -1
- data/lib/hecks/runtime/dispatch_claim.rb +72 -0
- data/lib/hecks/runtime/dispatch_context.rb +41 -0
- data/lib/hecks/runtime/dispatcher/planning.rb +24 -0
- data/lib/hecks/runtime/dispatcher/queries.rb +17 -0
- data/lib/hecks/runtime/dispatcher/routing.rb +40 -12
- data/lib/hecks/runtime/dispatcher/validating.rb +73 -0
- data/lib/hecks/runtime/dispatcher/value_routing.rb +54 -0
- data/lib/hecks/runtime/dispatcher.rb +46 -24
- data/lib/hecks/runtime/dry_run_plan.rb +60 -0
- data/lib/hecks/runtime/entity_element/location.rb +15 -8
- data/lib/hecks/runtime/entity_element/mutations.rb +4 -2
- data/lib/hecks/runtime/entity_interpreter/enforcement.rb +1 -1
- data/lib/hecks/runtime/entity_interpreter/locating.rb +6 -1
- data/lib/hecks/runtime/entity_interpreter/saving.rb +5 -5
- data/lib/hecks/runtime/entity_interpreter.rb +11 -5
- data/lib/hecks/runtime/errors.rb +56 -17
- data/lib/hecks/runtime/identity.rb +9 -0
- data/lib/hecks/runtime/instance.rb +3 -3
- data/lib/hecks/runtime/interpreting.rb +63 -6
- data/lib/hecks/runtime/invocation_builder.rb +7 -5
- data/lib/hecks/runtime/invocation_facts.rb +13 -8
- data/lib/hecks/runtime/loader/boot_steps.rb +3 -2
- data/lib/hecks/runtime/loader/described.rb +4 -0
- data/lib/hecks/runtime/loader.rb +52 -62
- data/lib/hecks/runtime/outbox/consumer_runner.rb +9 -1
- data/lib/hecks/runtime/outbox/redrive.rb +105 -0
- data/lib/hecks/runtime/outbox/relay.rb +36 -43
- data/lib/hecks/runtime/outbox/runnable.rb +62 -0
- data/lib/hecks/runtime/outbox.rb +21 -3
- data/lib/hecks/runtime/policy_interpreter/arguments.rb +7 -1
- data/lib/hecks/runtime/policy_interpreter/fan_out.rb +26 -37
- data/lib/hecks/runtime/policy_interpreter.rb +25 -9
- data/lib/hecks/runtime/port_operation_interpreter.rb +47 -19
- data/lib/hecks/runtime/query_interpreter/arguments.rb +6 -5
- data/lib/hecks/runtime/query_interpreter/outside_answer.rb +1 -1
- data/lib/hecks/runtime/query_interpreter/paging.rb +42 -0
- data/lib/hecks/runtime/query_interpreter/plugin_answer.rb +54 -0
- data/lib/hecks/runtime/query_interpreter/store_specification.rb +32 -0
- data/lib/hecks/runtime/query_interpreter.rb +16 -9
- data/lib/hecks/runtime/reaction_invocation/target_resolution.rb +10 -4
- data/lib/hecks/runtime/reaction_invocation.rb +5 -3
- data/lib/hecks/runtime/read_model_interpreter/head_ordering.rb +1 -1
- data/lib/hecks/runtime/read_model_interpreter/reductions.rb +23 -15
- data/lib/hecks/runtime/read_model_interpreter/shaping.rb +18 -7
- data/lib/hecks/runtime/read_model_interpreter.rb +5 -2
- data/lib/hecks/runtime/reference_hop.rb +3 -2
- data/lib/hecks/runtime/refusal_wording.rb +27 -7
- data/lib/hecks/runtime/registry/adapter_capabilities.rb +43 -0
- data/lib/hecks/runtime/registry/ask_verification.rb +15 -4
- data/lib/hecks/runtime/registry/{chapter_verification.rb → bluebook_verification.rb} +23 -23
- data/lib/hecks/runtime/registry/context_maps.rb +26 -0
- data/lib/hecks/runtime/registry/declarations.rb +38 -6
- data/lib/hecks/runtime/registry/durability_warnings.rb +18 -2
- data/lib/hecks/runtime/registry/hecksagon_rules.rb +42 -25
- data/lib/hecks/runtime/registry/plugin_query_verification.rb +45 -0
- data/lib/hecks/runtime/registry/providers.rb +34 -99
- data/lib/hecks/runtime/registry/query_verification.rb +30 -21
- data/lib/hecks/runtime/registry/runtime_state.rb +14 -3
- data/lib/hecks/runtime/registry/saga_persistence.rb +7 -16
- data/lib/hecks/runtime/registry/verification.rb +9 -5
- data/lib/hecks/runtime/registry/world_defaults.rb +31 -6
- data/lib/hecks/runtime/registry/world_names.rb +99 -0
- data/lib/hecks/runtime/registry.rb +84 -53
- data/lib/hecks/runtime/row_sweep.rb +104 -0
- data/lib/hecks/runtime/saga_interpreter/compensation.rb +15 -4
- data/lib/hecks/runtime/saga_interpreter/correlation.rb +29 -3
- data/lib/hecks/runtime/saga_interpreter/delivery.rb +13 -11
- data/lib/hecks/runtime/saga_interpreter/lifecycle.rb +1 -1
- data/lib/hecks/runtime/saga_interpreter/redrive.rb +141 -0
- data/lib/hecks/runtime/saga_interpreter/transitions.rb +35 -6
- data/lib/hecks/runtime/saga_interpreter.rb +82 -6
- data/lib/hecks/runtime/saga_pending_dispatch.rb +4 -2
- data/lib/hecks/runtime/scalar_field.rb +73 -0
- data/lib/hecks/runtime/tenant_scope.rb +2 -1
- data/lib/hecks/runtime/validation.rb +24 -0
- data/lib/hecks/runtime/value/admission.rb +11 -11
- data/lib/hecks/runtime/value/coercion.rb +43 -15
- data/lib/hecks/runtime/value/field_checks.rb +13 -13
- data/lib/hecks/runtime/value/invariant_violation.rb +4 -1
- data/lib/hecks/runtime/value/references.rb +7 -4
- data/lib/hecks/runtime/value/validation.rb +7 -4
- data/lib/hecks/runtime/wasm_plugin.rb +98 -0
- data/lib/hecks/runtime.rb +14 -14
- data/lib/hecks/rust_build/command_schemas.rb +27 -0
- data/lib/hecks/rust_build/conformance/surface.rb +50 -7
- data/lib/hecks/rust_build/conformance.rb +41 -8
- data/lib/hecks/rust_build/conformance_fuzz.rb +2 -2
- data/lib/hecks/rust_build/domain_name.rb +4 -1
- data/lib/hecks/rust_build/host.rb +15 -9
- data/lib/hecks/rust_build/native_build.rb +2 -1
- data/lib/hecks/rust_build/parser.rb +25 -0
- data/lib/hecks/rust_build/prebuilt_binary.rb +25 -5
- data/lib/hecks/rust_build/project_rust/codegen_run.rb +51 -17
- data/lib/hecks/rust_build/project_rust/declared.rb +80 -0
- data/lib/hecks/rust_build/project_rust/domain_loader.rb +15 -2
- data/lib/hecks/rust_build/project_rust/features_header.rb +22 -0
- data/lib/hecks/rust_build/project_rust/manifest.rb +96 -0
- data/lib/hecks/rust_build/project_rust/meta.rb +36 -0
- data/lib/hecks/rust_build/project_rust/root_mod/notes.rb +1 -1
- data/lib/hecks/rust_build/project_rust/target_ir.rb +12 -3
- data/lib/hecks/rust_build/project_rust.rb +31 -73
- data/lib/hecks/rust_build/target_dir.rb +40 -0
- data/lib/hecks/rust_build/wasm.rb +46 -17
- data/lib/hecks/rust_build/wasm_browser.rb +8 -2
- data/lib/hecks/rust_build/write_if_changed.rb +19 -4
- data/lib/hecks/rust_build.rb +31 -3
- data/lib/hecks/rust_format.rb +32 -2
- data/lib/hecks/secrets.rb +111 -0
- data/lib/hecks/sentence_form.rb +212 -0
- data/lib/hecks/served_options.rb +2 -1
- data/lib/hecks/shape_ledger/edge_hygiene.rb +170 -0
- data/lib/hecks/shape_ledger/scope.rb +51 -0
- data/lib/hecks/shape_ledger/status.rb +89 -0
- data/lib/hecks/shape_ledger.rb +194 -0
- data/lib/hecks/site/bluebook/site.bluebook +40 -62
- data/lib/hecks/site/bluebook/site.ports.hecksagon +1 -1
- data/lib/hecks/sme/bluebook/sme.bluebook +7 -1
- data/lib/hecks/storehouse/audit.rb +38 -5
- data/lib/hecks/storehouse/bluebook_words.rb +35 -0
- data/lib/hecks/storehouse/callers.rb +8 -0
- data/lib/hecks/storehouse/dispatching.rb +20 -6
- data/lib/hecks/storehouse/identity.rb +41 -0
- data/lib/hecks/storehouse/introspection.rb +4 -4
- data/lib/hecks/storehouse/querying.rb +9 -9
- data/lib/hecks/storehouse.rb +38 -10
- data/lib/hecks/switches.rb +21 -0
- data/lib/hecks/tenancy/bluebook/tenancy.bluebook +5 -9
- data/lib/hecks/text.rb +57 -0
- data/lib/hecks/tickets/bluebook/adapters/work_areas.adapter +7 -0
- data/lib/hecks/tickets/bluebook/adapters/work_limits.adapter +7 -0
- data/lib/hecks/tickets/bluebook/tickets.behaviors +70 -0
- data/lib/hecks/tickets/bluebook/tickets.bluebook +350 -76
- data/lib/hecks/tickets/bluebook/tickets.hecksagon +8 -1
- data/lib/hecks/tickets/bluebook/tickets.ports.hecksagon +18 -1
- data/lib/hecks/tools/argument_gate_matrix/dispatching.rb +1 -1
- data/lib/hecks/tools/argument_gate_matrix/rows.rb +7 -7
- data/lib/hecks/tools/argument_gate_matrix/violations.rb +1 -1
- data/lib/hecks/tools/argument_gate_matrix.rb +3 -3
- data/lib/hecks/{cli → tools}/behaviors.rb +1 -1
- data/lib/hecks/{cli → tools}/canonicalise.rb +1 -1
- data/lib/hecks/{cli → tools}/check_engine_agreement.rb +1 -1
- data/lib/hecks/{cli → tools}/check_era.rb +7 -7
- data/lib/hecks/tools/ci_gate_decision.rb +5 -2
- data/lib/hecks/tools/ci_gates.rb +9 -3
- data/lib/hecks/tools/ci_workflows.rb +98 -0
- data/lib/hecks/tools/comment_style/caps_words.rb +11 -8
- data/lib/hecks/tools/comment_style/dictionary.rb +7 -2
- data/lib/hecks/tools/comment_style/fixing.rb +10 -5
- data/lib/hecks/tools/comment_style/long_blocks.rb +1 -1
- data/lib/hecks/tools/comment_style/masking.rb +5 -5
- data/lib/hecks/tools/comment_style/run.rb +4 -1
- data/lib/hecks/tools/comment_style/source_file.rb +10 -3
- data/lib/hecks/tools/comment_style/structure_checks.rb +13 -4
- data/lib/hecks/tools/comment_style/text_checks.rb +1 -1
- data/lib/hecks/tools/comment_style.rb +25 -2
- data/lib/hecks/{cli → tools}/console.rb +12 -7
- data/lib/hecks/tools/deploy_recipe/generation.rb +1 -1
- data/lib/hecks/tools/deploy_recipe/targets.rb +1 -1
- data/lib/hecks/tools/deploy_recipe/worlds.rb +15 -9
- data/lib/hecks/tools/deploy_recipe.rb +17 -10
- data/lib/hecks/tools/deploy_recipe_lint/fixtures.rb +1 -1
- data/lib/hecks/tools/deploy_recipe_lint/makefile.rb +7 -1
- data/lib/hecks/{cli → tools}/deploy_template_diff.rb +1 -1
- data/lib/hecks/{cli → tools}/doc_coverage.rb +2 -2
- data/lib/hecks/{cli → tools}/document.rb +15 -10
- data/lib/hecks/{cli → tools}/domain_stub.rb +1 -1
- data/lib/hecks/{cli → tools}/domain_writer.rb +5 -2
- data/lib/hecks/tools/downstream_model_check.rb +122 -0
- data/lib/hecks/tools/drop_implicit_append_fields.rb +4 -1
- data/lib/hecks/tools/era_projection_backfill.rb +11 -6
- data/lib/hecks/tools/era_reattest.rb +11 -6
- data/lib/hecks/tools/evolve_run.rb +2 -2
- data/lib/hecks/tools/expected_era.rb +139 -0
- data/lib/hecks/{cli → tools}/expression_projection.rb +3 -3
- data/lib/hecks/{cli → tools}/follow.rb +4 -4
- data/lib/hecks/tools/fuzz_sweep/findings.rb +18 -2
- data/lib/hecks/tools/fuzz_sweep/options.rb +45 -6
- data/lib/hecks/tools/fuzz_sweep/pool.rb +12 -1
- data/lib/hecks/tools/fuzz_sweep/shards.rb +155 -0
- data/lib/hecks/tools/fuzz_sweep.rb +28 -10
- data/lib/hecks/tools/gate.rb +65 -34
- data/lib/hecks/tools/gate_memo/local_files.rb +33 -0
- data/lib/hecks/tools/gate_memo/rehearsal.rb +54 -0
- data/lib/hecks/tools/gate_memo/ruby_files.rb +152 -0
- data/lib/hecks/tools/gate_memo/session.rb +114 -0
- data/lib/hecks/tools/gate_memo/standalone.rb +67 -0
- data/lib/hecks/tools/gate_memo.rb +196 -0
- data/lib/hecks/tools/gate_report.rb +56 -0
- data/lib/hecks/{cli → tools}/history.rb +5 -2
- data/lib/hecks/tools/hoist_local_givens/rewriting.rb +3 -2
- data/lib/hecks/tools/hoist_local_givens.rb +2 -4
- data/lib/hecks/{cli → tools}/interview_agent.rb +1 -1
- data/lib/hecks/{cli → tools}/interview_aggregate.rb +1 -1
- data/lib/hecks/{cli → tools}/interview_draft.rb +9 -2
- data/lib/hecks/{cli → tools}/interview_record.rb +8 -4
- data/lib/hecks/{cli → tools}/interview_run.rb +9 -7
- data/lib/hecks/{cli → tools}/interview_session/agent_state.rb +1 -1
- data/lib/hecks/{cli → tools}/interview_session/catalog.rb +1 -1
- data/lib/hecks/{cli → tools}/interview_session/manual_findings.rb +1 -1
- data/lib/hecks/{cli → tools}/interview_session/proposals.rb +6 -3
- data/lib/hecks/{cli → tools}/interview_session.rb +5 -4
- data/lib/hecks/tools/interview_shape/lifecycle.rb +92 -0
- data/lib/hecks/{cli → tools}/interview_shape.rb +20 -74
- data/lib/hecks/{cli → tools}/ir.rb +1 -1
- data/lib/hecks/tools/lanes/files.rb +1 -1
- data/lib/hecks/tools/lanes/prebuilt_workflow.rb +1 -1
- data/lib/hecks/tools/lanes/rows.rb +9 -3
- data/lib/hecks/tools/lanes/ruleset.rb +1 -1
- data/lib/hecks/tools/lanes/watch_workflow.rb +7 -9
- data/lib/hecks/tools/lanes/workflow.rb +2 -2
- data/lib/hecks/tools/lanes.rb +2 -2
- data/lib/hecks/{cli → tools}/mcp.rb +1 -1
- data/lib/hecks/tools/model_check/examination.rb +142 -0
- data/lib/hecks/{cli → tools}/model_check/sources.rb +5 -3
- data/lib/hecks/{cli → tools}/model_check.rb +2 -2
- data/lib/hecks/tools/move_disables/reasons.rb +35 -0
- data/lib/hecks/tools/move_disables.rb +211 -0
- data/lib/hecks/tools/move_former_name.rb +105 -0
- data/lib/hecks/tools/oidc_manifests.rb +16 -19
- data/lib/hecks/{cli → tools}/pattern_cases.rb +1 -1
- data/lib/hecks/tools/persistence_notice.rb +45 -0
- data/lib/hecks/{cli → tools}/present.rb +2 -2
- data/lib/hecks/{cli → tools}/project_cli/launcher_source.rb +39 -27
- data/lib/hecks/{cli → tools}/project_cli/templates.rb +42 -14
- data/lib/hecks/{cli → tools}/project_cli.rb +22 -20
- data/lib/hecks/tools/project_diagrams.rb +59 -0
- data/lib/hecks/{cli → tools}/project_glossary.rb +16 -16
- data/lib/hecks/{cli → tools}/reference.rb +3 -3
- data/lib/hecks/{cli → tools}/refresh_projections.rb +17 -12
- data/lib/hecks/{cli → tools}/refresh_rspec_runtime_baseline.rb +1 -1
- data/lib/hecks/tools/regeneration_run/pool.rb +148 -0
- data/lib/hecks/tools/regeneration_run/scratch.rb +76 -0
- data/lib/hecks/tools/regeneration_run.rb +134 -107
- data/lib/hecks/{cli → tools}/release.rb +1 -1
- data/lib/hecks/{cli → tools}/release_gem.rb +1 -1
- data/lib/hecks/tools/release_only.rb +5 -2
- data/lib/hecks/{cli → tools}/rspec_io_parallel_files.rb +1 -1
- data/lib/hecks/{cli → tools}/rspec_shard_files.rb +1 -1
- data/lib/hecks/{cli → tools}/run.rb +36 -7
- data/lib/hecks/{cli → tools}/run_expectations.rb +1 -1
- data/lib/hecks/tools/rust_comment_style/caps_words.rb +5 -5
- data/lib/hecks/tools/rust_comment_style/fixing.rb +1 -1
- data/lib/hecks/tools/rust_comment_style/masking.rb +7 -4
- data/lib/hecks/tools/rust_comment_style/source_file.rb +7 -2
- data/lib/hecks/tools/rust_comment_style/text_checks.rb +1 -1
- data/lib/hecks/tools/rust_comment_style/tokenizer.rb +4 -1
- data/lib/hecks/{cli → tools}/seed_semantics_corpus.rb +18 -8
- data/lib/hecks/{cli → tools}/serve_query_ir_mcp.rb +1 -1
- data/lib/hecks/{cli → tools}/shape.rb +6 -6
- data/lib/hecks/tools/site_routes/editor_files.rb +16 -13
- data/lib/hecks/tools/site_routes/root_files.rb +14 -13
- data/lib/hecks/tools/site_routes/templates.rb +5 -4
- data/lib/hecks/tools/site_routes.rb +18 -15
- data/lib/hecks/{cli → tools}/smoke_http/checks.rb +1 -1
- data/lib/hecks/{cli → tools}/smoke_http/settings.rb +1 -1
- data/lib/hecks/{cli → tools}/smoke_http/transport.rb +4 -3
- data/lib/hecks/{cli → tools}/smoke_http.rb +1 -1
- data/lib/hecks/{cli → tools}/smoke_test.rb +1 -1
- data/lib/hecks/{cli → tools}/spec_example.rb +1 -1
- data/lib/hecks/tools/stage_progress.rb +54 -0
- data/lib/hecks/{cli → tools}/statements.rb +8 -8
- data/lib/hecks/tools/stores.rb +123 -0
- data/lib/hecks/{cli → tools}/stress_concurrency_specs/reporting.rb +5 -2
- data/lib/hecks/{cli → tools}/stress_concurrency_specs/usage.rb +4 -4
- data/lib/hecks/{cli → tools}/stress_concurrency_specs.rb +3 -3
- data/lib/hecks/tools/tail_merge.rb +3 -3
- data/lib/hecks/tools/tenant_provisioning.rb +29 -6
- data/lib/hecks/tools/tools_doc.rb +22 -20
- data/lib/hecks/tools/translation_audit/domain_loading.rb +3 -3
- data/lib/hecks/tools/translation_audit/edges.rb +21 -16
- data/lib/hecks/tools/translation_audit/verdicts.rb +1 -1
- data/lib/hecks/tools/translation_audit.rb +16 -5
- data/lib/hecks/tools/translation_scaffold/reporting.rb +4 -1
- data/lib/hecks/tools/translation_scaffold.rb +5 -15
- data/lib/hecks/tools.rb +4 -1
- data/lib/hecks/vendoring/git_source.rb +7 -4
- data/lib/hecks/version.rb +1 -1
- data/lib/hecks/vocabulary.rb +147 -10
- data/lib/hecks/workflow_pagination/judge.rb +48 -0
- data/lib/hecks/workflow_pagination/statement.rb +50 -0
- data/lib/hecks/workflow_pagination.rb +58 -0
- data/lib/hecks.rb +39 -18
- data/lib/rubocop/cop/hecks/bare_event_and_query_names.rb +58 -0
- data/lib/rubocop/cop/hecks/correlates_by_aggregate.rb +39 -0
- data/lib/rubocop/cop/hecks/creating_command_sets.rb +75 -0
- data/lib/rubocop/cop/hecks/inline_regexp.rb +29 -0
- data/lib/rubocop/cop/hecks/invariant_expression_language.rb +47 -0
- data/lib/rubocop/cop/hecks/lifecycle_needs_attribute.rb +58 -0
- data/lib/rubocop/cop/hecks/no_id_string_attribute.rb +49 -0
- data/lib/rubocop/cop/hecks/no_transport_in_runtime.rb +78 -0
- data/lib/rubocop/cop/hecks/one_terminal_mark_per_lifecycle.rb +47 -0
- data/lib/rubocop/cop/hecks/rails_model_rules.rb +46 -0
- data/lib/rubocop/cop/hecks/thread_shared_ivar_mutation.rb +1 -1
- data/lib/rubocop/cop/hecks/train_wreck.rb +69 -0
- data/lib/rubocop/cop/hecks/unnamed_pipeline.rb +51 -0
- data/lib/rubocop/cop/hecks/value_object_shorthand.rb +56 -0
- data/prebuilt/13c04bdc5970295b310f0a980d397ba722232c8da99cc1a0cdb383249921e871.marshal +0 -0
- data/prebuilt/e3857b77f47d9ecc1787b2e1f5136364f3cb3444ef4aea5479f3b98167affea2.marshal +0 -0
- data/qa/settings.yml +32 -0
- data/rust/.gitignore +0 -6
- data/rust/Cargo.lock +8 -1
- data/rust/Cargo.toml +8 -8
- data/rust/build/Cargo.toml +1 -1
- data/rust/build/src/binding_pass.rs +68 -19
- data/rust/build/src/cargo_sync.rs +1 -1
- data/rust/build/src/lineage_pass.rs +4 -71
- data/rust/build/src/pipeline.rs +104 -73
- data/rust/build/src/resolve.rs +86 -29
- data/rust/build/src/seams_pass.rs +117 -99
- data/rust/build/src/world_pass.rs +21 -12
- data/rust/codegen/Cargo.toml +1 -1
- data/rust/codegen/src/domain_generator.rs +27 -2
- data/rust/codegen/src/expr_emitter.rs +6 -0
- data/rust/codegen/src/json_codec.rs +230 -33
- data/rust/codegen/src/main.rs +35 -34
- data/rust/codegen/src/manifest.rs +1 -1
- data/rust/codegen/src/naming.rs +5 -5
- data/rust/codegen/src/one_of_references.rs +125 -0
- data/rust/codegen/src/presenters.rs +3 -3
- data/rust/codegen/src/queries.rs +76 -9
- data/rust/codegen/src/reactions.rs +19 -14
- data/rust/codegen/src/read_models.rs +19 -14
- data/rust/codegen/src/registry.rs +110 -20
- data/rust/codegen/src/sidecars.rs +1 -1
- data/rust/expr/Cargo.lock +54 -0
- data/rust/expr/Cargo.toml +52 -0
- data/rust/{src/kernel → expr/src}/attribute_shapes/composite.rs +45 -14
- data/rust/{src/kernel → expr/src}/attribute_shapes/list.rs +1 -1
- data/rust/{src/kernel → expr/src}/attribute_shapes/mod.rs +2 -2
- data/rust/{src/kernel → expr/src}/attribute_shapes/optional.rs +1 -1
- data/rust/{src/kernel → expr/src}/attribute_shapes/scalar.rs +3 -3
- data/rust/{src/kernel → expr/src}/expr.rs +30 -8
- data/rust/{src/kernel → expr/src}/expression_operators/arithmetic.rs +41 -18
- data/rust/{src/kernel → expr/src}/expression_operators/comparison.rs +54 -8
- data/rust/{src/kernel → expr/src}/expression_operators/enumeration.rs +73 -6
- data/rust/{src/kernel → expr/src}/expression_operators/logical.rs +2 -2
- data/rust/expr/src/expression_operators/membership.rs +33 -0
- data/rust/{src/kernel → expr/src}/expression_operators/mod.rs +2 -2
- data/rust/expr/src/expression_operators/pattern_match.rs +179 -0
- data/rust/{src/kernel → expr/src}/expression_operators/positional.rs +8 -6
- data/rust/{src/kernel → expr/src}/expression_operators/presence.rs +7 -6
- data/rust/{src/kernel → expr/src}/expression_operators/sign_test.rs +14 -4
- data/rust/{src/kernel → expr/src}/expression_operators/sized.rs +16 -9
- data/rust/{src/kernel → expr/src}/expression_operators/text.rs +30 -17
- data/rust/{src/kernel → expr/src}/expression_operators/to_string.rs +8 -6
- data/rust/expr/src/lib.rs +17 -0
- data/rust/expr/src/refusal.rs +62 -0
- data/rust/expr/src/rendering.rs +135 -0
- data/rust/{src/kernel → expr/src}/ruby_float.rs +7 -6
- data/rust/host/Cargo.lock +137 -123
- data/rust/host/Cargo.toml +8 -4
- data/rust/host/HECKS_RELEASE +1 -1
- data/rust/host/core/Cargo.toml +64 -0
- data/rust/host/core/clippy.toml +4 -0
- data/rust/host/core/src/arg_limits.rs +48 -0
- data/rust/host/core/src/clock.rs +146 -0
- data/rust/host/core/src/dispatch.rs +920 -0
- data/rust/host/core/src/expr_json.rs +1137 -0
- data/rust/host/{src → core/src}/host.rs +13 -14
- data/rust/host/{src → core/src}/ir.rs +227 -18
- data/rust/host/core/src/journal.rs +126 -0
- data/rust/host/{src → core/src}/lambda_client.rs +27 -96
- data/rust/host/core/src/lib.rs +37 -0
- data/rust/host/{src → core/src}/needs.rs +66 -20
- data/rust/host/core/src/plugins.rs +269 -0
- data/rust/host/core/src/presentation.rs +471 -0
- data/rust/host/{src → core/src}/presentation_write.rs +75 -43
- data/rust/host/{src → core/src}/query_step.rs +54 -16
- data/rust/host/{src → core/src}/reference_validate.rs +6 -6
- data/rust/host/{src → core/src}/storage_shape.rs +2 -2
- data/rust/host/core/src/store/memory.rs +635 -0
- data/rust/host/core/src/store.rs +418 -0
- data/rust/host/{src → core/src}/ui_schema.rs +8 -8
- data/rust/host/core/src/validation.rs +129 -0
- data/rust/host/core/src/wasm_runner.rs +410 -0
- data/rust/host/fuzz/.gitignore +4 -0
- data/rust/host/fuzz/Cargo.lock +4479 -0
- data/rust/host/fuzz/Cargo.toml +42 -0
- data/rust/host/fuzz/README.md +55 -0
- data/rust/host/fuzz/fuzz_targets/postgres_equivalence.rs +24 -0
- data/rust/host/fuzz/src/backend.rs +273 -0
- data/rust/host/fuzz/src/equivalence.rs +195 -0
- data/rust/host/fuzz/src/footprint.rs +182 -0
- data/rust/host/fuzz/src/lib.rs +17 -0
- data/rust/host/fuzz/src/ops.rs +345 -0
- data/rust/host/fuzz/tests/smoke.rs +50 -0
- data/rust/host/src/api.rs +316 -105
- data/rust/host/src/auth/google_signin.rs +183 -0
- data/rust/host/src/auth.rs +527 -370
- data/rust/host/src/aws_lambda_invoker.rs +61 -0
- data/rust/host/src/bin/expr_harness.rs +9 -4
- data/rust/host/src/bin/ledger.rs +84 -0
- data/rust/host/src/bin/lineage_harness.rs +17 -21
- data/rust/host/src/bin/mint_harness.rs +12 -36
- data/rust/host/src/bin/validate_harness.rs +1 -6
- data/rust/host/src/boot.rs +466 -174
- data/rust/host/src/boundary_fuzz/ir.rs +10 -5
- data/rust/host/src/boundary_fuzz/server.rs +6 -5
- data/rust/host/src/checkout.rs +4 -1
- data/rust/host/src/commerce_ir.rs +126 -105
- data/rust/host/src/{dispatch.rs → dispatch_tests.rs} +367 -847
- data/rust/host/src/extension.rs +3 -4
- data/rust/host/src/hecksagon_boundary_tests.rs +149 -0
- data/rust/host/src/ledger.rs +683 -0
- data/rust/host/src/lib.rs +21 -28
- data/rust/host/src/payments/tests.rs +136 -38
- data/rust/host/src/payments.rs +36 -32
- data/rust/host/src/platform_signin/extension.rs +1 -1
- data/rust/host/src/platform_signin/mapping_tests.rs +12 -9
- data/rust/host/src/platform_signin/session.rs +7 -14
- data/rust/host/src/platform_signin/verify.rs +0 -2
- data/rust/host/src/presentation_tests.rs +382 -0
- data/rust/host/src/rate_limit.rs +18 -2
- data/rust/host/src/server.rs +222 -43
- data/rust/host/src/{journal.rs → store/journal_sql.rs} +21 -127
- data/rust/host/src/{mint.rs → store/mint.rs} +52 -18
- data/rust/host/src/store/postgres.rs +493 -0
- data/rust/host/src/store.rs +97 -0
- data/rust/host/src/store_conformance.rs +1265 -0
- data/rust/host/src/web/commerce.rs +10 -18
- data/rust/host/src/web/newsletter/cooldown.rs +107 -22
- data/rust/host/src/web/newsletter/naming.rs +104 -0
- data/rust/host/src/web/newsletter/tests.rs +53 -0
- data/rust/host/src/web/newsletter.rs +73 -50
- data/rust/host/src/web/newsletter_send.rs +12 -13
- data/rust/host/src/web/registration_receipt.rs +2 -2
- data/rust/host/src/web/registrations.rs +754 -375
- data/rust/host/src/web/resend_webhook.rs +3 -3
- data/rust/host/src/web/signin_tests.rs +131 -0
- data/rust/host/src/web.rs +1479 -149
- data/rust/lsp/Cargo.toml +1 -1
- data/rust/lsp/README.md +27 -27
- data/rust/lsp/src/diagnostics.rs +75 -15
- data/rust/lsp/src/main.rs +101 -5
- data/rust/parser/Cargo.toml +1 -1
- data/rust/parser/fuzz/Cargo.toml +2 -2
- data/rust/parser/fuzz/README.md +3 -3
- data/rust/parser/fuzz/fuzz_targets/{chapter.rs → bluebook.rs} +4 -4
- data/rust/parser/fuzz/fuzz_targets/resolve.rs +2 -2
- data/rust/parser/src/build/correlation_keys.rs +125 -0
- data/rust/parser/src/build/mod.rs +1 -0
- data/rust/parser/src/build/named_pattern.rs +22 -0
- data/rust/parser/src/build/references.rs +52 -0
- data/rust/parser/src/diag.rs +22 -1
- data/rust/parser/src/emit.rs +122 -6
- data/rust/parser/src/expr/ast_json.rs +10 -0
- data/rust/parser/src/expr/resolver.rs +49 -9
- data/rust/parser/src/ir.rs +66 -7
- data/rust/parser/src/keywords.rs +704 -419
- data/rust/parser/src/lex.rs +115 -87
- data/rust/parser/src/main.rs +182 -32
- data/rust/parser/src/parse/adapter.rs +28 -0
- data/rust/parser/src/parse/aggregate.rs +64 -35
- data/rust/parser/src/parse/audience.rs +65 -0
- data/rust/parser/src/parse/command.rs +114 -15
- data/rust/parser/src/parse/{chapter.rs → declaration.rs} +182 -54
- data/rust/parser/src/parse/domain_port.rs +66 -9
- data/rust/parser/src/parse/entity.rs +35 -32
- data/rust/parser/src/parse/file.rs +1 -1
- data/rust/parser/src/parse/hecksagon.rs +15 -5
- data/rust/parser/src/parse/mod.rs +85 -2
- data/rust/parser/src/parse/process_manager.rs +18 -11
- data/rust/parser/src/parse/query.rs +13 -3
- data/rust/parser/src/parse/read_model.rs +2 -1
- data/rust/parser/src/parse/world.rs +12 -8
- data/rust/parser/src/phrases.rs +1258 -0
- data/rust/parser/src/render_ruby.rs +549 -0
- data/rust/parser/src/sentence/changes.rs +437 -0
- data/rust/parser/src/sentence/command_step.rs +206 -0
- data/rust/parser/src/sentence/complete.rs +276 -0
- data/rust/parser/src/sentence/entity.rs +41 -0
- data/rust/parser/src/sentence/examples.rs +1012 -0
- data/rust/parser/src/sentence/format.rs +885 -0
- data/rust/parser/src/sentence/lower.rs +1366 -0
- data/rust/parser/src/sentence/mod.rs +444 -0
- data/rust/parser/src/sentence/names.rs +69 -0
- data/rust/parser/src/sentence/options.rs +154 -0
- data/rust/parser/src/sentence/pattern.rs +433 -0
- data/rust/parser/src/sentence/process.rs +200 -0
- data/rust/parser/src/sentence/queries.rs +431 -0
- data/rust/parser/src/sentence/report.rs +283 -0
- data/rust/parser/src/sentence/token.rs +106 -0
- data/rust/parser/src/sentence/values.rs +498 -0
- data/rust/src/exemplar/json.rs +17 -0
- data/rust/src/exemplar/queries.rs +1 -0
- data/rust/src/exemplar/reactions.rs +1 -1
- data/rust/src/exemplar/registry.rs +30 -0
- data/rust/src/kernel/cli.rs +239 -15
- data/rust/src/kernel/collecting.rs +174 -0
- data/rust/src/kernel/dispatch.rs +59 -29
- data/rust/src/kernel/json.rs +62 -0
- data/rust/src/kernel/mod.rs +7 -67
- data/rust/src/kernel/named_query.rs +225 -11
- data/rust/src/kernel/orchestrate.rs +241 -28
- data/rust/src/kernel/orchestrate_guard_tests.rs +15 -12
- data/rust/src/kernel/query_cursor.rs +251 -0
- data/rust/src/kernel/query_ordering.rs +6 -6
- data/rust/src/kernel/read_model.rs +162 -17
- data/rust/src/kernel/repository.rs +107 -17
- data/rust/src/kernel/vocab/mod.rs +4 -0
- data/rust/src/kernel/vocab/reducer.rs +109 -0
- data/rust/src/kernel/vocab/refusal_class.rs +124 -0
- data/rust/src/kernel/vocab/refusal_template.rs +2 -2
- data/rust/src/lib.rs +1 -1
- data/rust/web/Cargo.lock +47 -0
- data/rust/web/Cargo.toml +1 -1
- metadata +689 -161
- data/lib/hecks/chapters.rb +0 -147
- data/lib/hecks/cli/command_table.rb +0 -72
- data/lib/hecks/cli/launcher_words.rb +0 -168
- data/lib/hecks/cli/model_check/examination.rb +0 -93
- data/lib/hecks/cli/project_diagrams.rb +0 -59
- data/lib/hecks/cli/stores.rb +0 -55
- data/lib/hecks/cli.rb +0 -133
- data/lib/hecks/gate/stages.yml +0 -234
- data/lib/hecks/hecks/codebase.bluebook +0 -3241
- data/lib/hecks/hecks/custodian.bluebook +0 -904
- data/lib/hecks/hecks/governance.world +0 -7
- data/lib/hecks/hecks/rehearsal.bluebook +0 -471
- data/lib/hecks/hecks/tooling.bluebook +0 -1238
- data/lib/hecks/ports/persistence/plugins/era/expected_era.rb +0 -137
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/boot.rb +0 -43
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/domain_rename.rb +0 -91
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/era_store.rb +0 -67
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/context.rb +0 -38
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/era_resolver.rb +0 -100
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/minter.rb +0 -102
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/refusals.rb +0 -51
- data/lib/hecks/ports/persistence/postgres_dump/connection.rb +0 -59
- data/lib/hecks/ports/persistence/postgres_dump.rb +0 -100
- data/lib/hecks/projections/deploy/box/templates/tunnel-egress.yaml.tmpl +0 -11
- data/lib/hecks/projector/exporter/capability_values.rb +0 -55
- data/lib/hecks/quality_control/adapters/clearance_recorder.rb +0 -36
- data/lib/hecks/quality_control/adapters/github_ci_webhook.rb +0 -56
- data/lib/hecks/runtime/remote_dispatcher.rb +0 -176
- data/prebuilt/ad68f9e06c8068c2a4176b57abf3083d591d5d8980c2b3e897b97331f965b643.marshal +0 -0
- data/prebuilt/ee22ec8642c4006717ba2b80f46e5efb81de459eef9dfec043e838c0ad4fd720.marshal +0 -0
- data/prebuilt/verdicts-b69f5dcbba79c20daa0493476fa7c317004a30aa2bc3d3dc6a722ea70ae2b43b.json +0 -1
- data/rust/host/src/clock.rs +0 -8
- data/rust/host/src/expr_json.rs +0 -1983
- data/rust/host/src/presentation.rs +0 -842
- data/rust/host/src/wasm_runner.rs +0 -206
- data/rust/src/kernel/expression_operators/membership.rs +0 -20
- data/rust/src/kernel/expression_operators/pattern_match.rs +0 -108
- /data/lib/hecks/{ports/persistence/plugins/era → adapters/driven}/postgres_era/events.rb +0 -0
- /data/lib/hecks/{ports/persistence/plugins/era → adapters/driven}/postgres_era/head_writes.rb +0 -0
- /data/lib/hecks/{ports/persistence/plugins/era → adapters/driven}/postgres_era/lineage/base_sql.rb +0 -0
- /data/lib/hecks/{ports/persistence/plugins/era → adapters/driven}/postgres_era/lineage/field_cache.rb +0 -0
- /data/lib/hecks/{ports/persistence/plugins/era → adapters/driven}/postgres_era/lineage/head_compiler.rb +0 -0
- /data/lib/hecks/{ports/persistence/plugins/era → adapters/driven}/postgres_era/lineage/merge_writes.rb +0 -0
- /data/lib/hecks/{ports/persistence/plugins/era → adapters/driven}/postgres_era/lineage/resumable_backfill.rb +0 -0
- /data/lib/hecks/{ports/persistence/plugins/era → adapters/driven}/postgres_era/lineage/transform_installer.rb +0 -0
- /data/lib/hecks/{ports/persistence/plugins/era → adapters/driven}/postgres_era/lineage/transform_sql.rb +0 -0
- /data/lib/hecks/{ports/persistence/plugins/era → adapters/driven}/postgres_era/sagas.rb +0 -0
- /data/rust/host/{src → core/src}/field_hints.rs +0 -0
- /data/rust/host/{src → core/src}/log.rs +0 -0
- /data/rust/host/{src → core/src}/reference_transform.rs +0 -0
data/rust/host/src/auth.rs
CHANGED
|
@@ -5,17 +5,24 @@ use crate::b64;
|
|
|
5
5
|
use crate::crypto::{constant_time_eq, hex_encode};
|
|
6
6
|
use crate::dispatch;
|
|
7
7
|
use crate::host::Host;
|
|
8
|
-
use crate::journal;
|
|
9
|
-
use crate::journal::LineageConfig;
|
|
10
8
|
use crate::lambda_client::LambdaInvoker;
|
|
9
|
+
use crate::needs::{Clock, ProcessClock};
|
|
10
|
+
use crate::store::journal_sql as journal;
|
|
11
|
+
use crate::store::journal_sql::LineageConfig;
|
|
12
|
+
use crate::store::{Store, StoreError};
|
|
11
13
|
use serde_json::{json, Value};
|
|
12
14
|
use std::path::Path;
|
|
13
15
|
use std::time::{SystemTime, UNIX_EPOCH};
|
|
14
|
-
|
|
15
|
-
|
|
16
|
+
|
|
17
|
+
mod google_signin;
|
|
18
|
+
|
|
19
|
+
pub use google_signin::GoogleSignin;
|
|
20
|
+
pub use google_signin::{
|
|
21
|
+
clear_header as google_signin_clear_header, read as read_google_signin,
|
|
22
|
+
set_header as google_signin_set_header,
|
|
23
|
+
};
|
|
16
24
|
|
|
17
25
|
const ISSUER: &str = "https://accounts.google.com";
|
|
18
|
-
const STATE_TTL_SECS: u64 = 600;
|
|
19
26
|
|
|
20
27
|
/// A verified Google ID token's claims.
|
|
21
28
|
pub struct Claims {
|
|
@@ -33,32 +40,33 @@ pub struct Session {
|
|
|
33
40
|
pub role: Option<String>,
|
|
34
41
|
}
|
|
35
42
|
|
|
36
|
-
|
|
43
|
+
/// The caller a signed-in session dispatches as: the role it carries and the identity
|
|
44
|
+
/// Governance holds assignments for. No session is an unidentified caller.
|
|
45
|
+
pub fn caller(session: Option<&Session>) -> crate::dispatch::Caller<'_> {
|
|
46
|
+
session.map_or_else(crate::dispatch::Caller::default, |session| {
|
|
47
|
+
crate::dispatch::Caller {
|
|
48
|
+
role: session.role.as_deref(),
|
|
49
|
+
actor_id: Some(session.identity_id.as_str()).filter(|id| !id.is_empty()),
|
|
50
|
+
}
|
|
51
|
+
})
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/// Google's consent page for one sign-in: `signin`'s `state` comes back on the callback and its
|
|
55
|
+
/// `nonce` inside the ID token, and both must match the cookie `/auth/google` set beside them.
|
|
56
|
+
pub fn authorization_url(redirect_uri: &str, signin: &GoogleSignin) -> Result<String, String> {
|
|
37
57
|
let client_id = crate::secrets::var("GOOGLE_CLIENT_ID")
|
|
38
58
|
.map_err(|_| "GOOGLE_CLIENT_ID not set".to_string())?;
|
|
39
|
-
let payload = now_secs().to_string();
|
|
40
|
-
let state = format!("{payload}.{}", sign(secret, &payload));
|
|
41
59
|
Ok(format!(
|
|
42
|
-
"https://accounts.google.com/o/oauth2/v2/auth?client_id={}&redirect_uri={}&response_type=code&scope={}&state={}",
|
|
60
|
+
"https://accounts.google.com/o/oauth2/v2/auth?client_id={}&redirect_uri={}&response_type=code&scope={}&state={}&nonce={}",
|
|
43
61
|
urlencode(&client_id),
|
|
44
62
|
urlencode(redirect_uri),
|
|
45
63
|
urlencode("openid email profile"),
|
|
46
|
-
urlencode(&state),
|
|
64
|
+
urlencode(&signin.state),
|
|
65
|
+
urlencode(&signin.nonce),
|
|
47
66
|
))
|
|
48
67
|
}
|
|
49
68
|
|
|
50
|
-
|
|
51
|
-
// is the whole check: a forged, mismatched, or expired state fails on it.
|
|
52
|
-
pub fn verify_state(state: &str, secret: &str) -> Result<(), String> {
|
|
53
|
-
let payload = verify_sig(secret, state).ok_or_else(|| "state mismatch".to_string())?;
|
|
54
|
-
let minted: u64 = payload.parse().map_err(|_| "state mismatch".to_string())?;
|
|
55
|
-
if now_secs().saturating_sub(minted) > STATE_TTL_SECS {
|
|
56
|
-
return Err("state expired".to_string());
|
|
57
|
-
}
|
|
58
|
-
Ok(())
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
pub async fn verify(code: &str, redirect_uri: &str) -> Result<Claims, String> {
|
|
69
|
+
pub async fn verify(code: &str, redirect_uri: &str, nonce: &str) -> Result<Claims, String> {
|
|
62
70
|
let client_id = crate::secrets::var("GOOGLE_CLIENT_ID")
|
|
63
71
|
.map_err(|_| "GOOGLE_CLIENT_ID not set".to_string())?;
|
|
64
72
|
let client_secret = crate::secrets::var("GOOGLE_CLIENT_SECRET")
|
|
@@ -91,13 +99,13 @@ pub async fn verify(code: &str, redirect_uri: &str) -> Result<Claims, String> {
|
|
|
91
99
|
.and_then(|v| v.as_str())
|
|
92
100
|
.ok_or_else(|| "no id_token in the response".to_string())?;
|
|
93
101
|
|
|
94
|
-
verify_id_token(id_token, &client_id).await
|
|
102
|
+
verify_id_token(id_token, &client_id, nonce).await
|
|
95
103
|
}
|
|
96
104
|
|
|
97
105
|
// Fetched fresh, never cached: this is a real sign-in, not a hot path,
|
|
98
106
|
// and missing Google's own key rotation would be worse than one extra
|
|
99
107
|
// HTTPS round trip per login.
|
|
100
|
-
async fn verify_id_token(id_token: &str, client_id: &str) -> Result<Claims, String> {
|
|
108
|
+
async fn verify_id_token(id_token: &str, client_id: &str, nonce: &str) -> Result<Claims, String> {
|
|
101
109
|
let header =
|
|
102
110
|
jsonwebtoken::decode_header(id_token).map_err(|e| format!("bad id_token header: {e}"))?;
|
|
103
111
|
let kid = header
|
|
@@ -140,6 +148,7 @@ async fn verify_id_token(id_token: &str, client_id: &str) -> Result<Claims, Stri
|
|
|
140
148
|
let token = jsonwebtoken::decode::<Value>(id_token, &decoding_key, &validation)
|
|
141
149
|
.map_err(|err| format!("id_token signature/claims invalid: {err}"))?;
|
|
142
150
|
let claims = token.claims;
|
|
151
|
+
check_nonce(&claims, nonce)?;
|
|
143
152
|
|
|
144
153
|
Ok(Claims {
|
|
145
154
|
issuer: claims
|
|
@@ -161,6 +170,17 @@ async fn verify_id_token(id_token: &str, client_id: &str) -> Result<Claims, Stri
|
|
|
161
170
|
})
|
|
162
171
|
}
|
|
163
172
|
|
|
173
|
+
// The ID token must carry the nonce this browser's sign-in cookie holds, so a token minted for
|
|
174
|
+
// someone else's sign-in cannot finish this one.
|
|
175
|
+
fn check_nonce(claims: &Value, expected: &str) -> Result<(), String> {
|
|
176
|
+
let carried = claims.get("nonce").and_then(Value::as_str).unwrap_or("");
|
|
177
|
+
if constant_time_eq(carried.as_bytes(), expected.as_bytes()) {
|
|
178
|
+
Ok(())
|
|
179
|
+
} else {
|
|
180
|
+
Err("id_token nonce does not match this sign-in".to_string())
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
164
184
|
/// The cookie name the account token travels in when a deploy names none.
|
|
165
185
|
/// A deploy naming its own uses `HECKS_SESSION_COOKIE` instead.
|
|
166
186
|
pub const DEFAULT_ACCOUNT_COOKIE: &str = "hecks_session";
|
|
@@ -234,6 +254,39 @@ fn purpose_key(secret: &str, purpose: &str) -> String {
|
|
|
234
254
|
format!("{purpose}:{secret}")
|
|
235
255
|
}
|
|
236
256
|
|
|
257
|
+
const HANDOFF_PURPOSE: &str = "google-handoff";
|
|
258
|
+
const HANDOFF_TTL_SECS: u64 = 60;
|
|
259
|
+
|
|
260
|
+
/// A one-minute, single-use stand-in for `email`'s account token. It rides the redirect onto a
|
|
261
|
+
/// site on another origin, where a URL is logged and kept in history, and the site trades it for
|
|
262
|
+
/// the account token server-to-server (`redeem_handoff_token`).
|
|
263
|
+
pub fn handoff_token(secret: &str, email: &str) -> String {
|
|
264
|
+
let jti = uuid::Uuid::new_v4().simple().to_string();
|
|
265
|
+
purpose_token(
|
|
266
|
+
secret,
|
|
267
|
+
HANDOFF_PURPOSE,
|
|
268
|
+
json!({ "email": email, "jti": jti }),
|
|
269
|
+
HANDOFF_TTL_SECS,
|
|
270
|
+
)
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
/// The account token a handoff token stands for, the first time it is redeemed; `None` when it is
|
|
274
|
+
/// forged, expired, minted for another purpose, or already spent in `spent`.
|
|
275
|
+
pub fn redeem_handoff_token(
|
|
276
|
+
secret: &str,
|
|
277
|
+
token: &str,
|
|
278
|
+
spent: &crate::platform_signin::jti::JtiStore,
|
|
279
|
+
) -> Option<String> {
|
|
280
|
+
let claims = verify_purpose_token(secret, HANDOFF_PURPOSE, token)?;
|
|
281
|
+
let email = claims.get("email")?.as_str()?;
|
|
282
|
+
let jti = claims.get("jti")?.as_str()?;
|
|
283
|
+
let expires_at = i64::try_from(claims.get("exp")?.as_u64()?).ok()?;
|
|
284
|
+
let now = i64::try_from(now_secs()).ok()?;
|
|
285
|
+
spent
|
|
286
|
+
.claim(jti, expires_at + 1, now)
|
|
287
|
+
.then(|| account_token(secret, email, SESSION_TTL_SECS))
|
|
288
|
+
}
|
|
289
|
+
|
|
237
290
|
/// How long a session cookie, and the account token minted beside it, stays valid.
|
|
238
291
|
pub const SESSION_TTL_SECS: u64 = 60 * 60 * 24 * 14;
|
|
239
292
|
|
|
@@ -302,7 +355,7 @@ fn identity_id_from_state(state: &Value) -> Option<String> {
|
|
|
302
355
|
}
|
|
303
356
|
|
|
304
357
|
/// Resolves an external identity the way the Google callback does: the PostgresEra head first,
|
|
305
|
-
/// then the flat journal's `instances`. A domain whose Identity
|
|
358
|
+
/// then the flat journal's `instances`. A domain whose Identity bluebook keeps no head of its own
|
|
306
359
|
/// (its heads cover its own aggregates and the membership aggregate) holds the link only there.
|
|
307
360
|
///
|
|
308
361
|
/// # Errors
|
|
@@ -311,18 +364,18 @@ fn identity_id_from_state(state: &Value) -> Option<String> {
|
|
|
311
364
|
/// head that cannot be read is logged and skipped, as the Google callback does, so the flat journal
|
|
312
365
|
/// still gets its turn.
|
|
313
366
|
pub async fn resolve_identity_anywhere(
|
|
314
|
-
|
|
367
|
+
store: &Store,
|
|
315
368
|
wasm_path: &Path,
|
|
316
369
|
domain_ir: &Value,
|
|
317
370
|
issuer: &str,
|
|
318
371
|
subject: &str,
|
|
319
372
|
) -> anyhow::Result<Option<String>> {
|
|
320
|
-
match resolve_identity_from_head(
|
|
373
|
+
match resolve_identity_from_head(store, domain_ir, issuer, subject).await {
|
|
321
374
|
Ok(Some(found)) => return Ok(Some(found)),
|
|
322
375
|
Ok(None) => {}
|
|
323
376
|
Err(error) => eprintln!("resolve_identity: identity head: {error:#}"),
|
|
324
377
|
}
|
|
325
|
-
let read = match dispatch::read(
|
|
378
|
+
let read = match dispatch::read(store, wasm_path).await {
|
|
326
379
|
Ok(read) => read,
|
|
327
380
|
Err(error) if flat_journal_is_absent(&error) => return Ok(None),
|
|
328
381
|
Err(error) => return Err(error),
|
|
@@ -338,14 +391,14 @@ pub async fn resolve_identity_anywhere(
|
|
|
338
391
|
fn flat_journal_is_absent(error: &anyhow::Error) -> bool {
|
|
339
392
|
error
|
|
340
393
|
.chain()
|
|
341
|
-
.filter_map(|cause| cause.downcast_ref::<
|
|
342
|
-
.any(
|
|
394
|
+
.filter_map(|cause| cause.downcast_ref::<StoreError>())
|
|
395
|
+
.any(StoreError::is_not_provisioned)
|
|
343
396
|
}
|
|
344
397
|
|
|
345
398
|
/// Identity lives in the PostgresEra head, not `dispatch::read`'s
|
|
346
399
|
/// `instances` — tries both the domain-qualified and the bare relation name.
|
|
347
400
|
pub async fn resolve_identity_from_head(
|
|
348
|
-
|
|
401
|
+
store: &Store,
|
|
349
402
|
domain_ir: &Value,
|
|
350
403
|
issuer: &str,
|
|
351
404
|
subject: &str,
|
|
@@ -354,36 +407,21 @@ pub async fn resolve_identity_from_head(
|
|
|
354
407
|
return Ok(None);
|
|
355
408
|
};
|
|
356
409
|
let domain = domain_ir.get("name").and_then(|v| v.as_str()).unwrap_or("");
|
|
357
|
-
let
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
let sql = format!(
|
|
366
|
-
"SELECT state FROM {} WHERE state->'issuer'->>'value' = $1 AND state->'subject'->>'value' = $2",
|
|
367
|
-
journal::quote_ident(&name)
|
|
368
|
-
);
|
|
369
|
-
match guard.query_opt(&sql, &[&issuer, &subject]).await {
|
|
370
|
-
Ok(Some(row)) => {
|
|
371
|
-
let state: Value = row.get(0);
|
|
372
|
-
return Ok(identity_id_from_state(&state));
|
|
373
|
-
}
|
|
374
|
-
Ok(None) => return Ok(None),
|
|
375
|
-
Err(e) if e.code() == Some(&tokio_postgres::error::SqlState::UNDEFINED_TABLE) => {}
|
|
376
|
-
Err(e) => return Err(e.into()),
|
|
377
|
-
}
|
|
378
|
-
}
|
|
379
|
-
Ok(None)
|
|
410
|
+
let found = store
|
|
411
|
+
.head_find(
|
|
412
|
+
&[domain, "Identity"],
|
|
413
|
+
"external_identifier",
|
|
414
|
+
&[("issuer", issuer), ("subject", subject)],
|
|
415
|
+
)
|
|
416
|
+
.await?;
|
|
417
|
+
Ok(found.and_then(|state| identity_id_from_state(&state)))
|
|
380
418
|
}
|
|
381
419
|
|
|
382
420
|
// Member lives in the PostgresEra head, not `dispatch::read`'s journal
|
|
383
421
|
// `instances` — reading `instances` for an already-linked member misses
|
|
384
422
|
// it entirely, so this queries the head view directly instead.
|
|
385
423
|
async fn member_row_by_email(
|
|
386
|
-
|
|
424
|
+
store: &Store,
|
|
387
425
|
domain_ir: &Value,
|
|
388
426
|
email: &str,
|
|
389
427
|
) -> anyhow::Result<Option<Value>> {
|
|
@@ -391,27 +429,22 @@ async fn member_row_by_email(
|
|
|
391
429
|
// docs/decisions/0059: `head_view` needs the domain name too, the
|
|
392
430
|
// same one `ir.rs`/`web.rs` already extract this way.
|
|
393
431
|
let domain = domain_ir.get("name").and_then(|v| v.as_str()).unwrap_or("");
|
|
394
|
-
|
|
395
|
-
journal::read_lineage_head_by_id(&*guard, domain, &storage_name, email).await
|
|
432
|
+
Ok(store.head_by_id(domain, &storage_name, email).await?)
|
|
396
433
|
}
|
|
397
434
|
|
|
398
|
-
async fn member_rows(
|
|
399
|
-
client: &Mutex<Client>,
|
|
400
|
-
domain_ir: &Value,
|
|
401
|
-
) -> anyhow::Result<Vec<(String, Value)>> {
|
|
435
|
+
async fn member_rows(store: &Store, domain_ir: &Value) -> anyhow::Result<Vec<(String, Value)>> {
|
|
402
436
|
let (_, storage_name) = membership_aggregate(domain_ir)?;
|
|
403
437
|
let domain = domain_ir.get("name").and_then(|v| v.as_str()).unwrap_or("");
|
|
404
|
-
|
|
405
|
-
journal::read_lineage_head_all(&*guard, domain, &storage_name).await
|
|
438
|
+
Ok(store.head_all(domain, &storage_name).await?)
|
|
406
439
|
}
|
|
407
440
|
|
|
408
441
|
// The membership aggregate comes from `ir.json`'s declared `membership`
|
|
409
442
|
// key, never an env var or a lookup against lineage_capable_aggregates —
|
|
410
|
-
// the
|
|
443
|
+
// the bluebook declaration is the fact, same as Governance's authorization.
|
|
411
444
|
fn membership_aggregate(domain_ir: &Value) -> anyhow::Result<(String, String)> {
|
|
412
445
|
let provider = crate::ir::membership_provider(domain_ir).ok_or_else(|| {
|
|
413
446
|
anyhow::anyhow!(
|
|
414
|
-
"this domain attaches no
|
|
447
|
+
"this domain attaches no bluebook that provides \"membership\" — cannot resolve who may sign in"
|
|
415
448
|
)
|
|
416
449
|
})?;
|
|
417
450
|
Ok(membership_names(&provider.aggregate))
|
|
@@ -423,7 +456,7 @@ fn membership_names(aggregate: &str) -> (String, String) {
|
|
|
423
456
|
.next()
|
|
424
457
|
.unwrap_or(aggregate)
|
|
425
458
|
.to_string();
|
|
426
|
-
let storage_name = crate::
|
|
459
|
+
let storage_name = crate::store::journal_sql::snake(&bare);
|
|
427
460
|
(bare, storage_name)
|
|
428
461
|
}
|
|
429
462
|
|
|
@@ -431,60 +464,40 @@ fn membership_names(aggregate: &str) -> (String, String) {
|
|
|
431
464
|
// + snapshot-upsert transaction every Member write uses. The advisory
|
|
432
465
|
// lock guards a concurrent domain rename, not ordinal uniqueness.
|
|
433
466
|
async fn append_member_state(
|
|
434
|
-
|
|
467
|
+
store: &Store,
|
|
435
468
|
config: &LineageConfig,
|
|
436
469
|
domain_ir: &Value,
|
|
437
470
|
id: &str,
|
|
438
471
|
state: &Value,
|
|
439
472
|
) -> anyhow::Result<()> {
|
|
440
473
|
let (aggregate_name, _) = membership_aggregate(domain_ir)?;
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
config,
|
|
453
|
-
&journal::Mutation {
|
|
454
|
-
aggregate: &aggregate_name,
|
|
455
|
-
id,
|
|
456
|
-
operation: "save",
|
|
457
|
-
state,
|
|
458
|
-
},
|
|
459
|
-
)
|
|
460
|
-
.await?;
|
|
461
|
-
|
|
462
|
-
txn.commit().await?;
|
|
474
|
+
store
|
|
475
|
+
.put_head(
|
|
476
|
+
config,
|
|
477
|
+
&journal::Mutation {
|
|
478
|
+
aggregate: &aggregate_name,
|
|
479
|
+
id,
|
|
480
|
+
operation: "save",
|
|
481
|
+
state,
|
|
482
|
+
},
|
|
483
|
+
)
|
|
484
|
+
.await?;
|
|
463
485
|
Ok(())
|
|
464
486
|
}
|
|
465
487
|
|
|
466
488
|
pub async fn session_for_member_by_identity(
|
|
467
|
-
|
|
489
|
+
store: &Store,
|
|
468
490
|
domain_ir: &Value,
|
|
469
491
|
identity_id: &str,
|
|
470
492
|
) -> anyhow::Result<Option<Session>> {
|
|
471
493
|
let (_, storage_name) = membership_aggregate(domain_ir)?;
|
|
472
494
|
// docs/decisions/0059 — same domain-qualification as above.
|
|
473
495
|
let domain = domain_ir.get("name").and_then(|v| v.as_str()).unwrap_or("");
|
|
474
|
-
let
|
|
475
|
-
|
|
476
|
-
.query_opt(
|
|
477
|
-
&format!(
|
|
478
|
-
"SELECT state FROM {} WHERE state->'identity_id'->>'value' = $1",
|
|
479
|
-
journal::quote_ident(&journal::head_view(domain, &storage_name))
|
|
480
|
-
),
|
|
481
|
-
&[&identity_id],
|
|
482
|
-
)
|
|
496
|
+
let row = store
|
|
497
|
+
.head_find(&[domain], &storage_name, &[("identity_id", identity_id)])
|
|
483
498
|
.await?;
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
let state: Value = r.get(0);
|
|
487
|
-
if is_disabled(&state) {
|
|
499
|
+
Ok(row.and_then(|state| {
|
|
500
|
+
if is_lapsed(&state) {
|
|
488
501
|
return None;
|
|
489
502
|
}
|
|
490
503
|
Some(Session {
|
|
@@ -504,7 +517,7 @@ pub async fn session_for_member_by_identity(
|
|
|
504
517
|
/// never creates one; Admit is a separate, real cap-table event.
|
|
505
518
|
#[allow(clippy::too_many_arguments)]
|
|
506
519
|
pub async fn provision(
|
|
507
|
-
|
|
520
|
+
store: &Store,
|
|
508
521
|
wasm_path: &Path,
|
|
509
522
|
config: &LineageConfig,
|
|
510
523
|
domain_ir: &Value,
|
|
@@ -513,10 +526,10 @@ pub async fn provision(
|
|
|
513
526
|
subject: &str,
|
|
514
527
|
invoker: &dyn LambdaInvoker,
|
|
515
528
|
) -> anyhow::Result<Option<Session>> {
|
|
516
|
-
let Some(member) = member_row_by_email(
|
|
529
|
+
let Some(member) = member_row_by_email(store, domain_ir, email).await? else {
|
|
517
530
|
return Ok(None);
|
|
518
531
|
};
|
|
519
|
-
if
|
|
532
|
+
if is_lapsed(&member) {
|
|
520
533
|
return Ok(None);
|
|
521
534
|
}
|
|
522
535
|
let role = member
|
|
@@ -532,10 +545,10 @@ pub async fn provision(
|
|
|
532
545
|
};
|
|
533
546
|
|
|
534
547
|
// Uses this domain's declared identity provider (`ir::identity_provider`),
|
|
535
|
-
// never a hard-coded
|
|
548
|
+
// never a hard-coded bluebook. Link's reference field is `identity`, not
|
|
536
549
|
// `identity_id` -- changing this silently breaks linking.
|
|
537
550
|
let Some(identity) = crate::ir::identity_provider(domain_ir) else {
|
|
538
|
-
anyhow::bail!("this domain attaches no
|
|
551
|
+
anyhow::bail!("this domain attaches no bluebook that provides \"identity\" — cannot register or link an identity");
|
|
539
552
|
};
|
|
540
553
|
|
|
541
554
|
// Reuse an Identity already registered+linked for this (issuer, subject)
|
|
@@ -551,7 +564,7 @@ pub async fn provision(
|
|
|
551
564
|
// first makes a retry converge onto the earlier attempt's identity
|
|
552
565
|
// instead of accumulating another orphan.
|
|
553
566
|
let identity_id = if let Some(existing) =
|
|
554
|
-
resolve_identity_from_head(
|
|
567
|
+
resolve_identity_from_head(store, domain_ir, issuer, subject).await?
|
|
555
568
|
{
|
|
556
569
|
existing
|
|
557
570
|
} else {
|
|
@@ -561,7 +574,7 @@ pub async fn provision(
|
|
|
561
574
|
// provisioning, not a caller-submitted command, so there is no
|
|
562
575
|
// caller role to assert.
|
|
563
576
|
let register = dispatch::handle(
|
|
564
|
-
Host::new(
|
|
577
|
+
Host::new(store, wasm_path, config, invoker),
|
|
565
578
|
&identity.register,
|
|
566
579
|
json!({"identity_id": {"value": identity_id}}),
|
|
567
580
|
None,
|
|
@@ -572,7 +585,7 @@ pub async fn provision(
|
|
|
572
585
|
}
|
|
573
586
|
|
|
574
587
|
let link_external = dispatch::handle(
|
|
575
|
-
Host::new(
|
|
588
|
+
Host::new(store, wasm_path, config, invoker),
|
|
576
589
|
&identity.link,
|
|
577
590
|
json!({
|
|
578
591
|
"identity": identity_id, "key": {"value": format!("{issuer}:{subject}")},
|
|
@@ -589,15 +602,15 @@ pub async fn provision(
|
|
|
589
602
|
};
|
|
590
603
|
|
|
591
604
|
// The grant verb this domain's declared authorization provider names
|
|
592
|
-
// (`ir::authorization_provider`), never a hard-coded
|
|
605
|
+
// (`ir::authorization_provider`), never a hard-coded bluebook.
|
|
593
606
|
let Some(provider) = crate::ir::authorization_provider(domain_ir) else {
|
|
594
607
|
anyhow::bail!(
|
|
595
|
-
"this domain attaches no
|
|
608
|
+
"this domain attaches no bluebook that provides \"authorization\" — cannot grant a role"
|
|
596
609
|
);
|
|
597
610
|
};
|
|
598
|
-
let assign_role = dispatch::handle(Host::new(
|
|
611
|
+
let assign_role = dispatch::handle(Host::new(store, wasm_path, config, invoker), &provider.grant, json!({
|
|
599
612
|
"actor_id": {"value": identity_id}, "role_name": {"value": role}, "scope": {"value": config.domain.clone()},
|
|
600
|
-
"starts_at": {"value":
|
|
613
|
+
"starts_at": {"value": crate::clock::iso8601_now()},
|
|
601
614
|
}), None).await?;
|
|
602
615
|
if !assign_role.accepted {
|
|
603
616
|
anyhow::bail!("{} refused: {}", provider.grant, assign_role.result);
|
|
@@ -608,7 +621,7 @@ pub async fn provision(
|
|
|
608
621
|
// refuse. Writes through the same journal+snapshot append instead.
|
|
609
622
|
let mut new_state = member.clone();
|
|
610
623
|
new_state["identity_id"] = json!({"value": identity_id});
|
|
611
|
-
append_member_state(
|
|
624
|
+
append_member_state(store, config, domain_ir, email, &new_state).await?;
|
|
612
625
|
|
|
613
626
|
let name = member
|
|
614
627
|
.get("name")
|
|
@@ -625,37 +638,87 @@ pub async fn provision(
|
|
|
625
638
|
}
|
|
626
639
|
|
|
627
640
|
pub async fn grant_access(
|
|
628
|
-
|
|
641
|
+
store: &Store,
|
|
629
642
|
wasm_path: &Path,
|
|
630
643
|
config: &LineageConfig,
|
|
631
644
|
domain_ir: &Value,
|
|
632
645
|
email: &str,
|
|
633
646
|
role: &str,
|
|
634
647
|
) -> anyhow::Result<bool> {
|
|
635
|
-
let
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
648
|
+
let outcome =
|
|
649
|
+
grant_access_until(store, wasm_path, config, domain_ir, email, role, None).await?;
|
|
650
|
+
Ok(outcome == GrantOutcome::Done)
|
|
651
|
+
}
|
|
639
652
|
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
653
|
+
/// What `grant_access_until` decided.
|
|
654
|
+
#[derive(Debug, PartialEq, Eq)]
|
|
655
|
+
pub enum GrantOutcome {
|
|
656
|
+
/// The role (and window, if any) is now on the person's row.
|
|
657
|
+
Done,
|
|
658
|
+
/// No person with that email exists.
|
|
659
|
+
UnknownPerson,
|
|
660
|
+
/// The change would leave no active admin without an end date.
|
|
661
|
+
LastOpenAdmin,
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
/// Grants `role` like `grant_access`, for a window ending at `expires_at`
|
|
665
|
+
/// (an ISO 8601 UTC instant) when one is given. Granting again renews: the
|
|
666
|
+
/// new window replaces the old, and `None` clears it, so a plain grant makes
|
|
667
|
+
/// the access open-ended. Refused when it would take the site's last
|
|
668
|
+
/// open-ended admin off that footing, locked with other membership writes.
|
|
669
|
+
pub async fn grant_access_until(
|
|
670
|
+
store: &Store,
|
|
671
|
+
wasm_path: &Path,
|
|
672
|
+
config: &LineageConfig,
|
|
673
|
+
domain_ir: &Value,
|
|
674
|
+
email: &str,
|
|
675
|
+
role: &str,
|
|
676
|
+
expires_at: Option<&str>,
|
|
677
|
+
) -> anyhow::Result<GrantOutcome> {
|
|
678
|
+
let _ = wasm_path;
|
|
679
|
+
let (aggregate_name, storage_name) = membership_aggregate(domain_ir)?;
|
|
680
|
+
let domain = domain_ir.get("name").and_then(|v| v.as_str()).unwrap_or("");
|
|
681
|
+
let target = email.trim().to_lowercase();
|
|
682
|
+
|
|
683
|
+
let outcome = store
|
|
684
|
+
.amend_head(config, domain, &aggregate_name, &storage_name, |rows| {
|
|
685
|
+
let Some((id, member)) = rows.iter().find(|(id, _)| id.to_lowercase() == target) else {
|
|
686
|
+
return (GrantOutcome::UnknownPerson, None);
|
|
687
|
+
};
|
|
688
|
+
|
|
689
|
+
// Same append protocol `provision` uses. GrantAccess's own bluebook
|
|
690
|
+
// command sets only `:role`; the window is a host-side field beside it,
|
|
691
|
+
// like `disabled`, so it needs no bluebook attribute.
|
|
692
|
+
let mut new_state = member.clone();
|
|
693
|
+
new_state["role"] = json!({"value": role});
|
|
694
|
+
match expires_at {
|
|
695
|
+
Some(instant) => new_state[EXPIRY_FIELD] = json!(instant),
|
|
696
|
+
None => {
|
|
697
|
+
if let Some(fields) = new_state.as_object_mut() {
|
|
698
|
+
fields.remove(EXPIRY_FIELD);
|
|
699
|
+
}
|
|
700
|
+
}
|
|
701
|
+
}
|
|
702
|
+
if strands_open_admin(rows, id, &new_state) {
|
|
703
|
+
return (GrantOutcome::LastOpenAdmin, None);
|
|
704
|
+
}
|
|
705
|
+
(GrantOutcome::Done, Some((id.clone(), new_state)))
|
|
706
|
+
})
|
|
707
|
+
.await?;
|
|
708
|
+
Ok(outcome)
|
|
646
709
|
}
|
|
647
710
|
|
|
648
711
|
/// Admits a new person (name + email), the same state `Admit` sets.
|
|
649
712
|
/// Returns `false` without writing when that email already exists.
|
|
650
713
|
pub async fn admit_person(
|
|
651
|
-
|
|
714
|
+
store: &Store,
|
|
652
715
|
config: &LineageConfig,
|
|
653
716
|
domain_ir: &Value,
|
|
654
717
|
email: &str,
|
|
655
718
|
name: &str,
|
|
656
719
|
) -> anyhow::Result<bool> {
|
|
657
720
|
let email = email.to_lowercase();
|
|
658
|
-
let exists = member_rows(
|
|
721
|
+
let exists = member_rows(store, domain_ir)
|
|
659
722
|
.await?
|
|
660
723
|
.into_iter()
|
|
661
724
|
.any(|(id, _)| id.to_lowercase() == email);
|
|
@@ -663,7 +726,7 @@ pub async fn admit_person(
|
|
|
663
726
|
return Ok(false);
|
|
664
727
|
}
|
|
665
728
|
let state = json!({"name": {"value": name}, "email": {"value": email}});
|
|
666
|
-
append_member_state(
|
|
729
|
+
append_member_state(store, config, domain_ir, &email, &state).await?;
|
|
667
730
|
Ok(true)
|
|
668
731
|
}
|
|
669
732
|
|
|
@@ -676,6 +739,77 @@ fn is_disabled(state: &Value) -> bool {
|
|
|
676
739
|
.unwrap_or(false)
|
|
677
740
|
}
|
|
678
741
|
|
|
742
|
+
// The row field holding the end of a time-boxed access window, an ISO 8601
|
|
743
|
+
// UTC instant. It sits beside `disabled` in the row state, so a grant that
|
|
744
|
+
// carries no window leaves the person with open-ended access as before.
|
|
745
|
+
const EXPIRY_FIELD: &str = "access_expires_at";
|
|
746
|
+
|
|
747
|
+
// The window's end as written on the row: a bare string, or a `{"value": ..}`
|
|
748
|
+
// wrapper as other timestamp attributes carry.
|
|
749
|
+
enum Window {
|
|
750
|
+
// No window is set (absent or null).
|
|
751
|
+
Open,
|
|
752
|
+
// A window ending at this many epoch seconds.
|
|
753
|
+
Ends(i64),
|
|
754
|
+
// A window is set but is not a readable instant; it counts as ended.
|
|
755
|
+
Unreadable,
|
|
756
|
+
}
|
|
757
|
+
|
|
758
|
+
fn window_of(state: &Value) -> Window {
|
|
759
|
+
let Some(raw) = state.get(EXPIRY_FIELD).filter(|v| !v.is_null()) else {
|
|
760
|
+
return Window::Open;
|
|
761
|
+
};
|
|
762
|
+
let text = raw.get("value").unwrap_or(raw).as_str();
|
|
763
|
+
text.and_then(crate::clock::parse_iso_utc)
|
|
764
|
+
.map_or(Window::Unreadable, Window::Ends)
|
|
765
|
+
}
|
|
766
|
+
|
|
767
|
+
// The window's end for display: the stored text, exactly as written.
|
|
768
|
+
fn expiry_text(state: &Value) -> Option<String> {
|
|
769
|
+
let raw = state.get(EXPIRY_FIELD).filter(|v| !v.is_null())?;
|
|
770
|
+
let text = raw.get("value").unwrap_or(raw);
|
|
771
|
+
Some(
|
|
772
|
+
text.as_str()
|
|
773
|
+
.map_or_else(|| text.to_string(), str::to_string),
|
|
774
|
+
)
|
|
775
|
+
}
|
|
776
|
+
|
|
777
|
+
// Whether the row's access window has ended at `now` (epoch seconds). An
|
|
778
|
+
// unreadable value counts as ended: access fails closed.
|
|
779
|
+
fn is_expired_at(state: &Value, now: i64) -> bool {
|
|
780
|
+
match window_of(state) {
|
|
781
|
+
Window::Open => false,
|
|
782
|
+
Window::Unreadable => true,
|
|
783
|
+
Window::Ends(ends) => ends <= now,
|
|
784
|
+
}
|
|
785
|
+
}
|
|
786
|
+
|
|
787
|
+
// Whether the row grants no access right now: disabled, or its window ended.
|
|
788
|
+
// Every check that lets a person in or counts an admin asks this, never
|
|
789
|
+
// `is_disabled` alone; disabling and enabling keep the raw flag.
|
|
790
|
+
fn is_lapsed(state: &Value) -> bool {
|
|
791
|
+
is_lapsed_at(state, ProcessClock.now_secs())
|
|
792
|
+
}
|
|
793
|
+
|
|
794
|
+
fn is_lapsed_at(state: &Value, now: i64) -> bool {
|
|
795
|
+
is_disabled(state) || is_expired_at(state, now)
|
|
796
|
+
}
|
|
797
|
+
|
|
798
|
+
/// The ISO 8601 UTC instant `days` days after `now` (epoch seconds), the
|
|
799
|
+
/// value `grant_access_until` stores as the end of a window.
|
|
800
|
+
pub fn expiry_after_days_from(days: u32, now: i64) -> String {
|
|
801
|
+
crate::clock::iso_utc((now + i64::from(days) * 86_400).max(0) as u64)
|
|
802
|
+
}
|
|
803
|
+
|
|
804
|
+
/// `expiry_after_days_from` counted from the process clock, which a spec
|
|
805
|
+
/// can fix (`needs::set_fixed_clock`).
|
|
806
|
+
pub fn expiry_after_days(days: u32) -> String {
|
|
807
|
+
expiry_after_days_from(days, ProcessClock.now_secs())
|
|
808
|
+
}
|
|
809
|
+
|
|
810
|
+
/// The longest window a grant may carry, in days.
|
|
811
|
+
pub const MAX_EXPIRY_DAYS: u32 = 365;
|
|
812
|
+
|
|
679
813
|
// A deleted person's row is never erased (this is an append-only journal,
|
|
680
814
|
// same as every other aggregate here) — "deleted" only means all_people
|
|
681
815
|
// stops listing it. There is deliberately no undelete route: re-admitting
|
|
@@ -708,9 +842,10 @@ pub fn is_admin_role(role: &str) -> bool {
|
|
|
708
842
|
ADMIN_ROLES.contains(&role)
|
|
709
843
|
}
|
|
710
844
|
|
|
711
|
-
// Whether the row holds an admin role ("Admin" or "Owner") and is not disabled
|
|
845
|
+
// Whether the row holds an admin role ("Admin" or "Owner") and is not disabled
|
|
846
|
+
// or past its access window.
|
|
712
847
|
fn is_active_admin(state: &Value) -> bool {
|
|
713
|
-
role_of(state).is_some_and(is_admin_role) && !
|
|
848
|
+
role_of(state).is_some_and(is_admin_role) && !is_lapsed(state)
|
|
714
849
|
}
|
|
715
850
|
|
|
716
851
|
// Whether `state` is the only active admin (Admin or Owner) among `rows`.
|
|
@@ -718,60 +853,82 @@ fn is_last_active_admin(rows: &[(String, Value)], state: &Value) -> bool {
|
|
|
718
853
|
is_active_admin(state) && rows.iter().filter(|(_, s)| is_active_admin(s)).count() <= 1
|
|
719
854
|
}
|
|
720
855
|
|
|
856
|
+
// Whether the row is an active admin with no end date: the kind of access a
|
|
857
|
+
// site keeps at least one of, because a window's lapse cannot be prevented.
|
|
858
|
+
fn is_open_admin(state: &Value) -> bool {
|
|
859
|
+
is_active_admin(state) && matches!(window_of(state), Window::Open)
|
|
860
|
+
}
|
|
861
|
+
|
|
862
|
+
// Whether replacing the row `id` with `after` would leave the site with no
|
|
863
|
+
// open-ended admin where it had one, or would make the site's first admin one
|
|
864
|
+
// with an end date. A site that has admins but none open-ended (it was never
|
|
865
|
+
// given one) is not made worse by a change, so this stays false there.
|
|
866
|
+
fn strands_open_admin(rows: &[(String, Value)], id: &str, after: &Value) -> bool {
|
|
867
|
+
let had = rows.iter().any(|(_, s)| is_open_admin(s));
|
|
868
|
+
let keeps = is_open_admin(after)
|
|
869
|
+
|| rows
|
|
870
|
+
.iter()
|
|
871
|
+
.any(|(other, s)| other.to_lowercase() != id.to_lowercase() && is_open_admin(s));
|
|
872
|
+
let first_admin = !rows
|
|
873
|
+
.iter()
|
|
874
|
+
.any(|(_, s)| role_of(s).is_some_and(is_admin_role));
|
|
875
|
+
let windowed_first = first_admin && role_of(after).is_some_and(is_admin_role) && !keeps;
|
|
876
|
+
(had && !keeps) || windowed_first
|
|
877
|
+
}
|
|
878
|
+
|
|
721
879
|
/// Whether the person with `email` (compared case-insensitively) is an
|
|
722
|
-
/// active admin: granted the "Admin" or "Owner" role and not
|
|
880
|
+
/// active admin: granted the "Admin" or "Owner" role, not disabled, and not
|
|
881
|
+
/// past its access window.
|
|
723
882
|
pub async fn caller_is_admin(
|
|
724
|
-
|
|
883
|
+
store: &Store,
|
|
725
884
|
domain_ir: &Value,
|
|
726
885
|
email: &str,
|
|
727
886
|
) -> anyhow::Result<bool> {
|
|
728
887
|
let email = email.to_lowercase();
|
|
729
|
-
Ok(member_rows(
|
|
888
|
+
Ok(member_rows(store, domain_ir)
|
|
730
889
|
.await?
|
|
731
890
|
.iter()
|
|
732
891
|
.any(|(id, state)| id.to_lowercase() == email && is_active_admin(state)))
|
|
733
892
|
}
|
|
734
893
|
|
|
735
894
|
/// Whether `email` may hold a session right now: admitted, granted a role,
|
|
736
|
-
/// and
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
domain_ir: &Value,
|
|
740
|
-
email: &str,
|
|
741
|
-
) -> anyhow::Result<bool> {
|
|
895
|
+
/// and neither disabled nor past its access window — a signature alone can't
|
|
896
|
+
/// tell, since a cookie outlives a later disable or the end of the window.
|
|
897
|
+
pub async fn has_access(store: &Store, domain_ir: &Value, email: &str) -> anyhow::Result<bool> {
|
|
742
898
|
let email = email.to_lowercase();
|
|
743
|
-
Ok(member_rows(
|
|
899
|
+
Ok(member_rows(store, domain_ir)
|
|
744
900
|
.await?
|
|
745
901
|
.iter()
|
|
746
902
|
.any(|(id, state)| {
|
|
747
|
-
id.to_lowercase() == email && role_of(state).is_some() && !
|
|
903
|
+
id.to_lowercase() == email && role_of(state).is_some() && !is_lapsed(state)
|
|
748
904
|
}))
|
|
749
905
|
}
|
|
750
906
|
|
|
751
907
|
/// The role `email` holds right now, or `None` if unknown, roleless, or
|
|
752
|
-
/// disabled — reads the live membership head, never the
|
|
908
|
+
/// disabled or past its window — reads the live membership head, never the
|
|
909
|
+
/// session cookie.
|
|
753
910
|
pub async fn active_role(
|
|
754
|
-
|
|
911
|
+
store: &Store,
|
|
755
912
|
domain_ir: &Value,
|
|
756
913
|
email: &str,
|
|
757
914
|
) -> anyhow::Result<Option<String>> {
|
|
758
915
|
let email = email.to_lowercase();
|
|
759
|
-
Ok(member_rows(
|
|
916
|
+
Ok(member_rows(store, domain_ir)
|
|
760
917
|
.await?
|
|
761
918
|
.iter()
|
|
762
|
-
.find(|(id, state)| id.to_lowercase() == email && !
|
|
919
|
+
.find(|(id, state)| id.to_lowercase() == email && !is_lapsed(state))
|
|
763
920
|
.and_then(|(_, state)| role_of(state).map(String::from)))
|
|
764
921
|
}
|
|
765
922
|
|
|
766
923
|
/// The Governance identity id linked to the member with `email`, read from
|
|
767
924
|
/// the live membership head. `None` when the member has not signed in yet.
|
|
768
925
|
pub async fn member_identity_id(
|
|
769
|
-
|
|
926
|
+
store: &Store,
|
|
770
927
|
domain_ir: &Value,
|
|
771
928
|
email: &str,
|
|
772
929
|
) -> anyhow::Result<Option<String>> {
|
|
773
930
|
let email = email.to_lowercase();
|
|
774
|
-
Ok(member_rows(
|
|
931
|
+
Ok(member_rows(store, domain_ir)
|
|
775
932
|
.await?
|
|
776
933
|
.iter()
|
|
777
934
|
.find(|(id, state)| id.to_lowercase() == email && !is_deleted(state))
|
|
@@ -788,10 +945,13 @@ fn linked_identity_id(state: &Value) -> Option<String> {
|
|
|
788
945
|
}
|
|
789
946
|
|
|
790
947
|
/// Every admitted person as a JSON row: name, email, role, `linked`
|
|
791
|
-
/// (signed in), `granted` (has access), `disabled
|
|
948
|
+
/// (signed in), `granted` (has access right now), `disabled` (the raw flag),
|
|
949
|
+
/// `access_expires_at` (the end of a time-boxed window, `null` when open-ended),
|
|
950
|
+
/// `expired` (the window has ended or is unreadable), and `identity_id`
|
|
792
951
|
/// (the Governance identity id, `null` until the person has signed in).
|
|
793
|
-
pub async fn all_people(
|
|
794
|
-
|
|
952
|
+
pub async fn all_people(store: &Store, domain_ir: &Value) -> anyhow::Result<Vec<Value>> {
|
|
953
|
+
let now = ProcessClock.now_secs();
|
|
954
|
+
Ok(member_rows(store, domain_ir)
|
|
795
955
|
.await?
|
|
796
956
|
.into_iter()
|
|
797
957
|
.filter(|(_, state)| !is_deleted(state))
|
|
@@ -802,13 +962,16 @@ pub async fn all_people(client: &Mutex<Client>, domain_ir: &Value) -> anyhow::Re
|
|
|
802
962
|
.and_then(|v| v.get("value"))
|
|
803
963
|
.is_some();
|
|
804
964
|
let disabled = is_disabled(&state);
|
|
965
|
+
let expired = is_expired_at(&state, now);
|
|
805
966
|
json!({
|
|
806
967
|
"name": state.get("name").and_then(|v| v.get("value")),
|
|
807
968
|
"email": state.get("email").and_then(|v| v.get("value")),
|
|
808
969
|
"role": role,
|
|
809
970
|
"linked": linked,
|
|
810
|
-
"granted": role.is_some() && !disabled,
|
|
971
|
+
"granted": role.is_some() && !disabled && !expired,
|
|
811
972
|
"disabled": disabled,
|
|
973
|
+
"access_expires_at": expiry_text(&state),
|
|
974
|
+
"expired": expired,
|
|
812
975
|
"identity_id": linked_identity_id(&state),
|
|
813
976
|
})
|
|
814
977
|
})
|
|
@@ -828,12 +991,14 @@ pub enum DisableOutcome {
|
|
|
828
991
|
UnknownPerson,
|
|
829
992
|
/// Disabling would leave no active Admin.
|
|
830
993
|
LastAdmin,
|
|
994
|
+
/// Disabling would leave no active Admin without an end date.
|
|
995
|
+
LastOpenAdmin,
|
|
831
996
|
}
|
|
832
997
|
|
|
833
998
|
/// Disables or enables `target` for `caller`, keeping role and identity
|
|
834
999
|
/// link — locked with other membership writes so concurrent disables can't both win.
|
|
835
1000
|
pub async fn set_person_disabled(
|
|
836
|
-
|
|
1001
|
+
store: &Store,
|
|
837
1002
|
config: &LineageConfig,
|
|
838
1003
|
domain_ir: &Value,
|
|
839
1004
|
caller: &str,
|
|
@@ -844,53 +1009,40 @@ pub async fn set_person_disabled(
|
|
|
844
1009
|
let domain = domain_ir.get("name").and_then(|v| v.as_str()).unwrap_or("");
|
|
845
1010
|
let (caller, target) = (caller.trim().to_lowercase(), target.trim().to_lowercase());
|
|
846
1011
|
|
|
847
|
-
let
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
};
|
|
868
|
-
if is_disabled(state) == disable {
|
|
869
|
-
return Ok(DisableOutcome::Done);
|
|
870
|
-
}
|
|
871
|
-
if disable && is_last_active_admin(&rows, state) {
|
|
872
|
-
return Ok(DisableOutcome::LastAdmin);
|
|
873
|
-
}
|
|
1012
|
+
let outcome = store
|
|
1013
|
+
.amend_head(config, domain, &aggregate_name, &storage_name, |rows| {
|
|
1014
|
+
if !rows
|
|
1015
|
+
.iter()
|
|
1016
|
+
.any(|(id, state)| id.to_lowercase() == caller && is_active_admin(state))
|
|
1017
|
+
{
|
|
1018
|
+
return (DisableOutcome::CallerNotAdmin, None);
|
|
1019
|
+
}
|
|
1020
|
+
if disable && caller == target {
|
|
1021
|
+
return (DisableOutcome::SelfDisable, None);
|
|
1022
|
+
}
|
|
1023
|
+
let Some((id, state)) = rows.iter().find(|(id, _)| id.to_lowercase() == target) else {
|
|
1024
|
+
return (DisableOutcome::UnknownPerson, None);
|
|
1025
|
+
};
|
|
1026
|
+
if is_disabled(state) == disable {
|
|
1027
|
+
return (DisableOutcome::Done, None);
|
|
1028
|
+
}
|
|
1029
|
+
if disable && is_last_active_admin(rows, state) {
|
|
1030
|
+
return (DisableOutcome::LastAdmin, None);
|
|
1031
|
+
}
|
|
874
1032
|
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
state: &new_state,
|
|
889
|
-
},
|
|
890
|
-
)
|
|
891
|
-
.await?;
|
|
892
|
-
txn.commit().await?;
|
|
893
|
-
Ok(DisableOutcome::Done)
|
|
1033
|
+
let mut new_state = state.clone();
|
|
1034
|
+
if disable {
|
|
1035
|
+
new_state["disabled"] = json!(true);
|
|
1036
|
+
} else if let Some(fields) = new_state.as_object_mut() {
|
|
1037
|
+
fields.remove("disabled");
|
|
1038
|
+
}
|
|
1039
|
+
if strands_open_admin(rows, id, &new_state) {
|
|
1040
|
+
return (DisableOutcome::LastOpenAdmin, None);
|
|
1041
|
+
}
|
|
1042
|
+
(DisableOutcome::Done, Some((id.clone(), new_state)))
|
|
1043
|
+
})
|
|
1044
|
+
.await?;
|
|
1045
|
+
Ok(outcome)
|
|
894
1046
|
}
|
|
895
1047
|
|
|
896
1048
|
/// What `set_person_deleted` decided.
|
|
@@ -911,7 +1063,7 @@ pub enum DeleteOutcome {
|
|
|
911
1063
|
/// stays in the journal (nothing here is ever erased) but `all_people` stops
|
|
912
1064
|
/// listing it. Locked with other membership writes the same as disable/role.
|
|
913
1065
|
pub async fn set_person_deleted(
|
|
914
|
-
|
|
1066
|
+
store: &Store,
|
|
915
1067
|
config: &LineageConfig,
|
|
916
1068
|
domain_ir: &Value,
|
|
917
1069
|
caller: &str,
|
|
@@ -921,46 +1073,30 @@ pub async fn set_person_deleted(
|
|
|
921
1073
|
let domain = domain_ir.get("name").and_then(|v| v.as_str()).unwrap_or("");
|
|
922
1074
|
let (caller, target) = (caller.trim().to_lowercase(), target.trim().to_lowercase());
|
|
923
1075
|
|
|
924
|
-
let
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
};
|
|
942
|
-
if is_deleted(state) {
|
|
943
|
-
return Ok(DeleteOutcome::Done);
|
|
944
|
-
}
|
|
945
|
-
if !is_disabled(state) {
|
|
946
|
-
return Ok(DeleteOutcome::NotDisabled);
|
|
947
|
-
}
|
|
1076
|
+
let outcome = store
|
|
1077
|
+
.amend_head(config, domain, &aggregate_name, &storage_name, |rows| {
|
|
1078
|
+
if !rows
|
|
1079
|
+
.iter()
|
|
1080
|
+
.any(|(id, state)| id.to_lowercase() == caller && is_active_admin(state))
|
|
1081
|
+
{
|
|
1082
|
+
return (DeleteOutcome::CallerNotAdmin, None);
|
|
1083
|
+
}
|
|
1084
|
+
let Some((id, state)) = rows.iter().find(|(id, _)| id.to_lowercase() == target) else {
|
|
1085
|
+
return (DeleteOutcome::UnknownPerson, None);
|
|
1086
|
+
};
|
|
1087
|
+
if is_deleted(state) {
|
|
1088
|
+
return (DeleteOutcome::Done, None);
|
|
1089
|
+
}
|
|
1090
|
+
if !is_disabled(state) {
|
|
1091
|
+
return (DeleteOutcome::NotDisabled, None);
|
|
1092
|
+
}
|
|
948
1093
|
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
aggregate: &aggregate_name,
|
|
956
|
-
id,
|
|
957
|
-
operation: "save",
|
|
958
|
-
state: &new_state,
|
|
959
|
-
},
|
|
960
|
-
)
|
|
961
|
-
.await?;
|
|
962
|
-
txn.commit().await?;
|
|
963
|
-
Ok(DeleteOutcome::Done)
|
|
1094
|
+
let mut new_state = state.clone();
|
|
1095
|
+
new_state["deleted"] = json!(true);
|
|
1096
|
+
(DeleteOutcome::Done, Some((id.clone(), new_state)))
|
|
1097
|
+
})
|
|
1098
|
+
.await?;
|
|
1099
|
+
Ok(outcome)
|
|
964
1100
|
}
|
|
965
1101
|
|
|
966
1102
|
/// What `set_person_role` decided.
|
|
@@ -974,12 +1110,14 @@ pub enum RoleOutcome {
|
|
|
974
1110
|
UnknownPerson,
|
|
975
1111
|
/// The change would leave no active Admin or Owner.
|
|
976
1112
|
LastAdmin,
|
|
1113
|
+
/// The change would leave no active Admin or Owner without an end date.
|
|
1114
|
+
LastOpenAdmin,
|
|
977
1115
|
}
|
|
978
1116
|
|
|
979
1117
|
/// Sets `target`'s role on `caller`'s behalf; any active admin may
|
|
980
1118
|
/// grant any role, Owner included, locked against concurrent demotions.
|
|
981
1119
|
pub async fn set_person_role(
|
|
982
|
-
|
|
1120
|
+
store: &Store,
|
|
983
1121
|
config: &LineageConfig,
|
|
984
1122
|
domain_ir: &Value,
|
|
985
1123
|
caller: &str,
|
|
@@ -990,77 +1128,59 @@ pub async fn set_person_role(
|
|
|
990
1128
|
let domain = domain_ir.get("name").and_then(|v| v.as_str()).unwrap_or("");
|
|
991
1129
|
let (caller, target) = (caller.trim().to_lowercase(), target.trim().to_lowercase());
|
|
992
1130
|
|
|
993
|
-
let
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
};
|
|
1011
|
-
if role_of(state) == Some(role) {
|
|
1012
|
-
return Ok(RoleOutcome::Done);
|
|
1013
|
-
}
|
|
1014
|
-
if !is_admin_role(role) && is_last_active_admin(&rows, state) {
|
|
1015
|
-
return Ok(RoleOutcome::LastAdmin);
|
|
1016
|
-
}
|
|
1131
|
+
let outcome = store
|
|
1132
|
+
.amend_head(config, domain, &aggregate_name, &storage_name, |rows| {
|
|
1133
|
+
if !rows
|
|
1134
|
+
.iter()
|
|
1135
|
+
.any(|(id, state)| id.to_lowercase() == caller && is_active_admin(state))
|
|
1136
|
+
{
|
|
1137
|
+
return (RoleOutcome::CallerNotAdmin, None);
|
|
1138
|
+
}
|
|
1139
|
+
let Some((id, state)) = rows.iter().find(|(id, _)| id.to_lowercase() == target) else {
|
|
1140
|
+
return (RoleOutcome::UnknownPerson, None);
|
|
1141
|
+
};
|
|
1142
|
+
if role_of(state) == Some(role) {
|
|
1143
|
+
return (RoleOutcome::Done, None);
|
|
1144
|
+
}
|
|
1145
|
+
if !is_admin_role(role) && is_last_active_admin(rows, state) {
|
|
1146
|
+
return (RoleOutcome::LastAdmin, None);
|
|
1147
|
+
}
|
|
1017
1148
|
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
state: &new_state,
|
|
1028
|
-
},
|
|
1029
|
-
)
|
|
1030
|
-
.await?;
|
|
1031
|
-
txn.commit().await?;
|
|
1032
|
-
Ok(RoleOutcome::Done)
|
|
1149
|
+
let mut new_state = state.clone();
|
|
1150
|
+
new_state["role"] = json!({"value": role});
|
|
1151
|
+
if strands_open_admin(rows, id, &new_state) {
|
|
1152
|
+
return (RoleOutcome::LastOpenAdmin, None);
|
|
1153
|
+
}
|
|
1154
|
+
(RoleOutcome::Done, Some((id.clone(), new_state)))
|
|
1155
|
+
})
|
|
1156
|
+
.await?;
|
|
1157
|
+
Ok(outcome)
|
|
1033
1158
|
}
|
|
1034
1159
|
|
|
1035
|
-
/// Whether `
|
|
1036
|
-
///
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
)
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
};
|
|
1045
|
-
let prefix = format!("{}#", provider.assignment_aggregate);
|
|
1046
|
-
instances.as_object().is_some_and(|obj| {
|
|
1047
|
-
obj.iter().any(|(key, state)| {
|
|
1048
|
-
key.starts_with(&prefix)
|
|
1049
|
-
&& state
|
|
1050
|
-
.get("actor_id")
|
|
1051
|
-
.and_then(|v| v.get("value"))
|
|
1052
|
-
.and_then(|v| v.as_str())
|
|
1053
|
-
== Some(identity_id)
|
|
1054
|
-
&& state
|
|
1055
|
-
.get("role_name")
|
|
1056
|
-
.and_then(|v| v.get("value"))
|
|
1057
|
-
.and_then(|v| v.as_str())
|
|
1058
|
-
.is_some_and(is_admin_role)
|
|
1059
|
-
&& state.get("ends_at").is_none_or(serde_json::Value::is_null)
|
|
1060
|
-
})
|
|
1160
|
+
/// Whether any of `rows` — the provider's `live_assignments` answer for one actor — grants an
|
|
1161
|
+
/// Admin or Owner role. The bluebook's query decides which assignments are live, so this reads
|
|
1162
|
+
/// no `ends_at`.
|
|
1163
|
+
pub fn holds_admin(rows: &[Value]) -> bool {
|
|
1164
|
+
rows.iter().any(|row| {
|
|
1165
|
+
row.get("role_name")
|
|
1166
|
+
.and_then(|v| v.get("value"))
|
|
1167
|
+
.and_then(|v| v.as_str())
|
|
1168
|
+
.is_some_and(is_admin_role)
|
|
1061
1169
|
})
|
|
1062
1170
|
}
|
|
1063
1171
|
|
|
1172
|
+
/// The rows of a kernel query answer (`{"queries":[{"rows":[...]}]}`).
|
|
1173
|
+
pub fn answer_rows(answer: &Value) -> Vec<Value> {
|
|
1174
|
+
answer
|
|
1175
|
+
.get("queries")
|
|
1176
|
+
.and_then(|q| q.as_array())
|
|
1177
|
+
.and_then(|q| q.first())
|
|
1178
|
+
.and_then(|q| q.get("rows"))
|
|
1179
|
+
.and_then(|r| r.as_array())
|
|
1180
|
+
.cloned()
|
|
1181
|
+
.unwrap_or_default()
|
|
1182
|
+
}
|
|
1183
|
+
|
|
1064
1184
|
fn now_secs() -> u64 {
|
|
1065
1185
|
SystemTime::now()
|
|
1066
1186
|
.duration_since(UNIX_EPOCH)
|
|
@@ -1068,36 +1188,6 @@ fn now_secs() -> u64 {
|
|
|
1068
1188
|
.as_secs()
|
|
1069
1189
|
}
|
|
1070
1190
|
|
|
1071
|
-
// `dispatch.rs`'s `occurred_at` stamping reuses this exact rendering
|
|
1072
|
-
// rather than re-deriving it — see the body comment for the format
|
|
1073
|
-
// guarantee both call sites depend on.
|
|
1074
|
-
pub(crate) fn httpdate_now() -> String {
|
|
1075
|
-
// ISO 8601 UTC, matching Ruby's Time.now.utc.iso8601 -- Governance::
|
|
1076
|
-
// RoleAssignment.starts_at only needs to parse as a real instant,
|
|
1077
|
-
// never re-derived from or compared against wall-clock time here.
|
|
1078
|
-
let secs = now_secs();
|
|
1079
|
-
let days = secs / 86_400;
|
|
1080
|
-
let rem = secs % 86_400;
|
|
1081
|
-
let (h, m, s) = (rem / 3600, (rem % 3600) / 60, rem % 60);
|
|
1082
|
-
let (y, mo, d) = civil_from_days(days as i64);
|
|
1083
|
-
format!("{y:04}-{mo:02}-{d:02}T{h:02}:{m:02}:{s:02}Z")
|
|
1084
|
-
}
|
|
1085
|
-
|
|
1086
|
-
// Howard Hinnant's days-from-civil algorithm, inverted -- avoids
|
|
1087
|
-
// pulling in a full date/time crate for one timestamp format.
|
|
1088
|
-
const fn civil_from_days(z: i64) -> (i64, u32, u32) {
|
|
1089
|
-
let z = z + 719_468;
|
|
1090
|
-
let era = if z >= 0 { z } else { z - 146_096 } / 146_097;
|
|
1091
|
-
let doe = (z - era * 146_097) as u64;
|
|
1092
|
-
let yoe = (doe - doe / 1460 + doe / 36524 - doe / 146_096) / 365;
|
|
1093
|
-
let y = yoe as i64 + era * 400;
|
|
1094
|
-
let doy = doe - (365 * yoe + yoe / 4 - yoe / 100);
|
|
1095
|
-
let mp = (5 * doy + 2) / 153;
|
|
1096
|
-
let d = (doy - (153 * mp + 2) / 5 + 1) as u32;
|
|
1097
|
-
let m = if mp < 10 { mp + 3 } else { mp - 9 } as u32;
|
|
1098
|
-
(if m <= 2 { y + 1 } else { y }, m, d)
|
|
1099
|
-
}
|
|
1100
|
-
|
|
1101
1191
|
#[allow(clippy::expect_used)] // reason: HMAC accepts a key of any length, so construction cannot fail
|
|
1102
1192
|
fn sign(secret: &str, payload: &str) -> String {
|
|
1103
1193
|
use hmac::{Hmac, Mac};
|
|
@@ -1136,6 +1226,8 @@ pub(crate) fn urlencode(s: &str) -> String {
|
|
|
1136
1226
|
#[cfg(test)]
|
|
1137
1227
|
mod tests {
|
|
1138
1228
|
use super::*;
|
|
1229
|
+
#[allow(unused_imports)]
|
|
1230
|
+
use crate::store::StoreFixtures;
|
|
1139
1231
|
|
|
1140
1232
|
#[test]
|
|
1141
1233
|
fn signed_token_verifies_with_the_right_secret_and_rejects_tampering() {
|
|
@@ -1287,15 +1379,42 @@ mod tests {
|
|
|
1287
1379
|
}
|
|
1288
1380
|
|
|
1289
1381
|
#[test]
|
|
1290
|
-
fn
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
assert!(
|
|
1294
|
-
assert!(
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1382
|
+
fn an_id_token_is_accepted_only_with_the_sign_ins_own_nonce() {
|
|
1383
|
+
assert!(check_nonce(&json!({"nonce": "n-123"}), "n-123").is_ok());
|
|
1384
|
+
assert!(check_nonce(&json!({"nonce": "n-456"}), "n-123").is_err());
|
|
1385
|
+
assert!(check_nonce(&json!({}), "n-123").is_err());
|
|
1386
|
+
assert!(check_nonce(&json!({"nonce": 5}), "n-123").is_err());
|
|
1387
|
+
}
|
|
1388
|
+
|
|
1389
|
+
#[test]
|
|
1390
|
+
fn a_handoff_token_is_redeemed_once_for_the_account_token() {
|
|
1391
|
+
let spent = crate::platform_signin::jti::JtiStore::new();
|
|
1392
|
+
let handoff = handoff_token("s3cret", "zed@example.com");
|
|
1393
|
+
assert_eq!(verify_account_token("s3cret", &handoff), None);
|
|
1394
|
+
let account = redeem_handoff_token("s3cret", &handoff, &spent).unwrap();
|
|
1395
|
+
assert_eq!(
|
|
1396
|
+
verify_account_token("s3cret", &account).as_deref(),
|
|
1397
|
+
Some("zed@example.com")
|
|
1398
|
+
);
|
|
1399
|
+
assert_eq!(redeem_handoff_token("s3cret", &handoff, &spent), None);
|
|
1400
|
+
}
|
|
1401
|
+
|
|
1402
|
+
#[test]
|
|
1403
|
+
fn an_account_or_foreign_token_is_not_a_handoff_token() {
|
|
1404
|
+
let spent = crate::platform_signin::jti::JtiStore::new();
|
|
1405
|
+
let account = account_token("s3cret", "zed@example.com", 60);
|
|
1406
|
+
assert_eq!(redeem_handoff_token("s3cret", &account, &spent), None);
|
|
1407
|
+
let handoff = handoff_token("other-secret", "zed@example.com");
|
|
1408
|
+
assert_eq!(redeem_handoff_token("s3cret", &handoff, &spent), None);
|
|
1409
|
+
}
|
|
1410
|
+
|
|
1411
|
+
#[test]
|
|
1412
|
+
fn the_consent_url_carries_the_sign_ins_state_and_nonce() {
|
|
1413
|
+
crate::secrets::provide("GOOGLE_CLIENT_ID", "client-1".to_string());
|
|
1414
|
+
let signin = GoogleSignin::fresh();
|
|
1415
|
+
let url = authorization_url("https://host.example/auth/google/callback", &signin).unwrap();
|
|
1416
|
+
assert!(url.contains(&format!("&state={}", signin.state)), "{url}");
|
|
1417
|
+
assert!(url.contains(&format!("&nonce={}", signin.nonce)), "{url}");
|
|
1299
1418
|
}
|
|
1300
1419
|
|
|
1301
1420
|
#[test]
|
|
@@ -1312,7 +1431,7 @@ mod tests {
|
|
|
1312
1431
|
#[test]
|
|
1313
1432
|
fn resolve_identity_finds_a_link_that_only_the_flat_journal_holds() {
|
|
1314
1433
|
// The shape `ExternalIdentifier.Link` leaves in the flat snapshot of a domain whose Identity
|
|
1315
|
-
//
|
|
1434
|
+
// bluebook has no head of its own; the key carries the issuer URL and the subject.
|
|
1316
1435
|
let instances = json!({
|
|
1317
1436
|
"Identity::ExternalIdentifier#https://accounts.google.com:111": {
|
|
1318
1437
|
"identity": "471ef402-8609-4fa4-9288-6372fc74650d",
|
|
@@ -1356,21 +1475,61 @@ mod tests {
|
|
|
1356
1475
|
}
|
|
1357
1476
|
|
|
1358
1477
|
#[test]
|
|
1359
|
-
fn
|
|
1360
|
-
let
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1478
|
+
fn holds_admin_reads_the_live_assignment_rows() {
|
|
1479
|
+
let live = [json!({"actor_id": {"value": "id-1"}, "role_name": {"value": "Admin"}})];
|
|
1480
|
+
assert!(holds_admin(&live));
|
|
1481
|
+
// No live assignment answered: no role is held, whatever was granted before.
|
|
1482
|
+
assert!(!holds_admin(&[]));
|
|
1483
|
+
}
|
|
1484
|
+
|
|
1485
|
+
#[test]
|
|
1486
|
+
fn answer_rows_reads_the_first_query_of_a_kernel_answer() {
|
|
1487
|
+
let answer = json!({"queries": [{"query": "G.Live", "rows": [{"id": "a"}]}]});
|
|
1488
|
+
assert_eq!(answer_rows(&answer), vec![json!({"id": "a"})]);
|
|
1489
|
+
assert!(answer_rows(&json!({"queries": []})).is_empty());
|
|
1490
|
+
}
|
|
1491
|
+
|
|
1492
|
+
#[test]
|
|
1493
|
+
fn a_window_lapses_exactly_at_its_end_and_a_missing_one_never_does() {
|
|
1494
|
+
let ends = crate::clock::parse_iso_utc("2026-10-22T12:00:00Z").unwrap();
|
|
1495
|
+
let row = json!({"role": {"value": "Member"}, "access_expires_at": "2026-10-22T12:00:00Z"});
|
|
1496
|
+
assert!(!is_lapsed_at(&row, ends - 1), "one second before the end");
|
|
1497
|
+
assert!(is_lapsed_at(&row, ends), "the end instant itself is over");
|
|
1498
|
+
assert!(is_lapsed_at(&row, ends + 1));
|
|
1499
|
+
let open = json!({"role": {"value": "Member"}});
|
|
1500
|
+
assert!(!is_lapsed_at(&open, i64::MAX));
|
|
1501
|
+
let unreadable = json!({"access_expires_at": "next week"});
|
|
1502
|
+
assert!(is_lapsed_at(&unreadable, 0), "unreadable fails closed");
|
|
1503
|
+
let disabled = json!({"disabled": true, "access_expires_at": "2099-01-01T00:00:00Z"});
|
|
1504
|
+
assert!(
|
|
1505
|
+
is_lapsed_at(&disabled, 0),
|
|
1506
|
+
"an open window does not undo a disable"
|
|
1507
|
+
);
|
|
1508
|
+
}
|
|
1509
|
+
|
|
1510
|
+
#[test]
|
|
1511
|
+
fn expiry_after_days_counts_whole_days_from_the_given_instant() {
|
|
1512
|
+
let start = crate::clock::parse_iso_utc("2026-10-22T08:30:15Z").unwrap();
|
|
1513
|
+
assert_eq!(expiry_after_days_from(1, start), "2026-10-23T08:30:15Z");
|
|
1514
|
+
assert_eq!(
|
|
1515
|
+
expiry_after_days_from(MAX_EXPIRY_DAYS, start),
|
|
1516
|
+
"2027-10-22T08:30:15Z"
|
|
1517
|
+
);
|
|
1518
|
+
}
|
|
1365
1519
|
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1520
|
+
#[test]
|
|
1521
|
+
fn is_last_active_admin_ignores_an_admin_whose_window_ended() {
|
|
1522
|
+
let admin = json!({"role": {"value": "Admin"}});
|
|
1523
|
+
let expired =
|
|
1524
|
+
json!({"role": {"value": "Admin"}, "access_expires_at": "2020-01-01T00:00:00Z"});
|
|
1525
|
+
let rows = |states: &[&Value]| -> Vec<(String, Value)> {
|
|
1526
|
+
states
|
|
1527
|
+
.iter()
|
|
1528
|
+
.map(|s| (String::new(), (*s).clone()))
|
|
1529
|
+
.collect()
|
|
1369
1530
|
};
|
|
1370
|
-
assert!(
|
|
1371
|
-
assert!(!
|
|
1372
|
-
// No declared provider: no assignment can exist, so never admin.
|
|
1373
|
-
assert!(!holds_admin(&instances, "id-1", None));
|
|
1531
|
+
assert!(is_last_active_admin(&rows(&[&admin, &expired]), &admin));
|
|
1532
|
+
assert!(!is_last_active_admin(&rows(&[&admin, &expired]), &expired));
|
|
1374
1533
|
}
|
|
1375
1534
|
|
|
1376
1535
|
#[test]
|
|
@@ -1417,15 +1576,12 @@ mod tests {
|
|
|
1417
1576
|
|
|
1418
1577
|
#[test]
|
|
1419
1578
|
fn holds_admin_accepts_an_owner_assignment_and_refuses_other_roles() {
|
|
1420
|
-
let
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
assert!(holds_admin(&
|
|
1426
|
-
assert!(holds_admin(&assignment("Owner"), "id-1", Some(&provider)));
|
|
1427
|
-
assert!(!holds_admin(&assignment("Member"), "id-1", Some(&provider)));
|
|
1428
|
-
assert!(!holds_admin(&assignment("owner"), "id-1", Some(&provider)));
|
|
1579
|
+
let rows =
|
|
1580
|
+
|role: &str| [json!({"actor_id": {"value": "id-1"}, "role_name": {"value": role}})];
|
|
1581
|
+
assert!(holds_admin(&rows("Admin")));
|
|
1582
|
+
assert!(holds_admin(&rows("Owner")));
|
|
1583
|
+
assert!(!holds_admin(&rows("Member")));
|
|
1584
|
+
assert!(!holds_admin(&rows("owner")));
|
|
1429
1585
|
}
|
|
1430
1586
|
|
|
1431
1587
|
#[test]
|
|
@@ -1456,7 +1612,7 @@ mod tests {
|
|
|
1456
1612
|
// A throwaway Postgres database per test, matching the real
|
|
1457
1613
|
// `head_view` naming (`qualified_name(domain, "#{storage_name}_head")`,
|
|
1458
1614
|
// docs/decisions/0059) that the functions under test query directly.
|
|
1459
|
-
async fn scratch_member_db(name: &str) ->
|
|
1615
|
+
async fn scratch_member_db(name: &str) -> Store {
|
|
1460
1616
|
use tokio_postgres::NoTls;
|
|
1461
1617
|
let (admin, conn) = tokio_postgres::connect(&crate::test_pg::conninfo("postgres"), NoTls)
|
|
1462
1618
|
.await
|
|
@@ -1493,7 +1649,7 @@ mod tests {
|
|
|
1493
1649
|
)
|
|
1494
1650
|
.await
|
|
1495
1651
|
.unwrap();
|
|
1496
|
-
|
|
1652
|
+
crate::store::postgres_store(client)
|
|
1497
1653
|
}
|
|
1498
1654
|
|
|
1499
1655
|
// A minimal `ir.json`-shaped fixture: one lineage-capable aggregate
|
|
@@ -1592,6 +1748,7 @@ mod tests {
|
|
|
1592
1748
|
domain: "Acme".to_string(),
|
|
1593
1749
|
era: Some(1),
|
|
1594
1750
|
mirrored: None,
|
|
1751
|
+
seal_every: None,
|
|
1595
1752
|
};
|
|
1596
1753
|
{
|
|
1597
1754
|
let guard = db.lock().await;
|