trueppm-api 0.4.0b2__py3-none-any.whl
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.
- trueppm_api/__init__.py +14 -0
- trueppm_api/apps/access/__init__.py +0 -0
- trueppm_api/apps/access/apps.py +19 -0
- trueppm_api/apps/access/groups.py +518 -0
- trueppm_api/apps/access/management/__init__.py +0 -0
- trueppm_api/apps/access/management/commands/__init__.py +0 -0
- trueppm_api/apps/access/management/commands/create_admin.py +164 -0
- trueppm_api/apps/access/management/commands/migrate_locked.py +96 -0
- trueppm_api/apps/access/management/commands/revoke_api_tokens.py +209 -0
- trueppm_api/apps/access/migrations/0001_initial.py +61 -0
- trueppm_api/apps/access/migrations/0001_squashed_0012_programmembership_role_title.py +167 -0
- trueppm_api/apps/access/migrations/0002_alter_projectmembership_id.py +29 -0
- trueppm_api/apps/access/migrations/0003_projectmembership_soft_delete.py +24 -0
- trueppm_api/apps/access/migrations/0004_alter_projectmembership_role.py +34 -0
- trueppm_api/apps/access/migrations/0005_program_entity_and_membership.py +76 -0
- trueppm_api/apps/access/migrations/0006_role_ordinal_spacing.py +113 -0
- trueppm_api/apps/access/migrations/0007_projectmembership_joined_at_and_more.py +35 -0
- trueppm_api/apps/access/migrations/0008_projectmembership_source_group.py +25 -0
- trueppm_api/apps/access/migrations/0009_projectmembership_pm_proj_serverver_idx.py +20 -0
- trueppm_api/apps/access/migrations/0010_programmembership_joined_at_and_more.py +38 -0
- trueppm_api/apps/access/migrations/0011_alter_programmembership_unique_together_and_more.py +36 -0
- trueppm_api/apps/access/migrations/0012_programmembership_role_title.py +17 -0
- trueppm_api/apps/access/migrations/0013_userdefinedmentiongroup.py +82 -0
- trueppm_api/apps/access/migrations/0014_programmembership_progm_serverver_idx.py +19 -0
- trueppm_api/apps/access/migrations/0015_programuserdefinedmentiongroup.py +86 -0
- trueppm_api/apps/access/migrations/0016_externalstakeholder.py +67 -0
- trueppm_api/apps/access/migrations/0017_viewer_ordinal_one.py +132 -0
- trueppm_api/apps/access/migrations/0018_programmembership_sync_seq_and_more.py +52 -0
- trueppm_api/apps/access/migrations/0019_programmembership_reinstated_at_and_more.py +22 -0
- trueppm_api/apps/access/migrations/__init__.py +0 -0
- trueppm_api/apps/access/models.py +588 -0
- trueppm_api/apps/access/permissions.py +2837 -0
- trueppm_api/apps/access/serializers.py +946 -0
- trueppm_api/apps/access/services.py +667 -0
- trueppm_api/apps/access/signals.py +103 -0
- trueppm_api/apps/access/tasks.py +50 -0
- trueppm_api/apps/access/throttles.py +95 -0
- trueppm_api/apps/access/urls.py +191 -0
- trueppm_api/apps/access/views.py +1931 -0
- trueppm_api/apps/agents/__init__.py +6 -0
- trueppm_api/apps/agents/apps.py +11 -0
- trueppm_api/apps/agents/canonical.py +73 -0
- trueppm_api/apps/agents/deferred.py +108 -0
- trueppm_api/apps/agents/management/__init__.py +0 -0
- trueppm_api/apps/agents/management/commands/__init__.py +0 -0
- trueppm_api/apps/agents/management/commands/audit_prune.py +137 -0
- trueppm_api/apps/agents/management/commands/audit_verify.py +101 -0
- trueppm_api/apps/agents/middleware.py +44 -0
- trueppm_api/apps/agents/migrations/0001_initial.py +231 -0
- trueppm_api/apps/agents/migrations/0002_agentactioncheckpoint.py +71 -0
- trueppm_api/apps/agents/migrations/0003_agentactionrefusaldetail.py +58 -0
- trueppm_api/apps/agents/migrations/__init__.py +0 -0
- trueppm_api/apps/agents/models.py +371 -0
- trueppm_api/apps/agents/refusal.py +103 -0
- trueppm_api/apps/agents/serializers.py +85 -0
- trueppm_api/apps/agents/services.py +397 -0
- trueppm_api/apps/agents/signals.py +30 -0
- trueppm_api/apps/agents/urls.py +15 -0
- trueppm_api/apps/agents/views.py +108 -0
- trueppm_api/apps/csvimport/__init__.py +12 -0
- trueppm_api/apps/csvimport/apps.py +11 -0
- trueppm_api/apps/csvimport/mapping.py +363 -0
- trueppm_api/apps/csvimport/migrations/0001_initial.py +74 -0
- trueppm_api/apps/csvimport/migrations/0002_schedule_outline_batch_operations.py +37 -0
- trueppm_api/apps/csvimport/migrations/0003_csvimportrequest_date_order_and_more.py +26 -0
- trueppm_api/apps/csvimport/migrations/__init__.py +0 -0
- trueppm_api/apps/csvimport/models.py +111 -0
- trueppm_api/apps/csvimport/parser.py +1825 -0
- trueppm_api/apps/csvimport/services.py +48 -0
- trueppm_api/apps/csvimport/tasks.py +418 -0
- trueppm_api/apps/csvimport/template.py +37 -0
- trueppm_api/apps/csvimport/urls.py +50 -0
- trueppm_api/apps/csvimport/views.py +793 -0
- trueppm_api/apps/history/__init__.py +0 -0
- trueppm_api/apps/history/apps.py +12 -0
- trueppm_api/apps/history/changelog.py +359 -0
- trueppm_api/apps/history/diff_policy.py +177 -0
- trueppm_api/apps/history/serializers.py +104 -0
- trueppm_api/apps/history/signals.py +82 -0
- trueppm_api/apps/history/tasks.py +105 -0
- trueppm_api/apps/history/urls.py +38 -0
- trueppm_api/apps/history/views.py +443 -0
- trueppm_api/apps/idempotency/__init__.py +0 -0
- trueppm_api/apps/idempotency/apps.py +11 -0
- trueppm_api/apps/idempotency/migrations/0001_initial.py +71 -0
- trueppm_api/apps/idempotency/migrations/__init__.py +0 -0
- trueppm_api/apps/idempotency/mixins.py +226 -0
- trueppm_api/apps/idempotency/models.py +75 -0
- trueppm_api/apps/idempotency/tasks.py +61 -0
- trueppm_api/apps/integrations/__init__.py +13 -0
- trueppm_api/apps/integrations/apps.py +59 -0
- trueppm_api/apps/integrations/connections.py +806 -0
- trueppm_api/apps/integrations/encryption.py +92 -0
- trueppm_api/apps/integrations/external_sources.py +837 -0
- trueppm_api/apps/integrations/git_automation_services.py +231 -0
- trueppm_api/apps/integrations/git_webhook_auth.py +243 -0
- trueppm_api/apps/integrations/http.py +403 -0
- trueppm_api/apps/integrations/me_work.py +218 -0
- trueppm_api/apps/integrations/migrations/0001_initial.py +64 -0
- trueppm_api/apps/integrations/migrations/0001_squashed_0006_tasklink_description_tasklink_preview_type_and_more.py +187 -0
- trueppm_api/apps/integrations/migrations/0002_tasklink.py +67 -0
- trueppm_api/apps/integrations/migrations/0003_tasklink_tasklink_serverver_idx.py +17 -0
- trueppm_api/apps/integrations/migrations/0004_tasklink_custom_title_tasklink_labels.py +25 -0
- trueppm_api/apps/integrations/migrations/0005_boardautomation.py +56 -0
- trueppm_api/apps/integrations/migrations/0006_tasklink_description_tasklink_preview_type_and_more.py +40 -0
- trueppm_api/apps/integrations/migrations/0007_tasklink_created_at.py +23 -0
- trueppm_api/apps/integrations/migrations/0008_integrationcredential_config_externalworkitem.py +66 -0
- trueppm_api/apps/integrations/migrations/0009_externalsyncrequest_and_more.py +96 -0
- trueppm_api/apps/integrations/migrations/0010_externalworkitem_due_date.py +17 -0
- trueppm_api/apps/integrations/migrations/0011_tasklink_sync_seq_tasklink_tasklink_syncseq_idx.py +22 -0
- trueppm_api/apps/integrations/migrations/0012_boardautomation_last_delivery_at_and_more.py +42 -0
- trueppm_api/apps/integrations/migrations/0013_externalsyncrequest_attempt_count_and_more.py +26 -0
- trueppm_api/apps/integrations/migrations/__init__.py +0 -0
- trueppm_api/apps/integrations/models.py +622 -0
- trueppm_api/apps/integrations/notification_channels.py +57 -0
- trueppm_api/apps/integrations/opengraph.py +229 -0
- trueppm_api/apps/integrations/outgoing.py +201 -0
- trueppm_api/apps/integrations/providers.py +519 -0
- trueppm_api/apps/integrations/registry.py +345 -0
- trueppm_api/apps/integrations/serializers.py +453 -0
- trueppm_api/apps/integrations/services.py +171 -0
- trueppm_api/apps/integrations/tasks.py +731 -0
- trueppm_api/apps/integrations/throttles.py +165 -0
- trueppm_api/apps/integrations/urls.py +77 -0
- trueppm_api/apps/integrations/views.py +1106 -0
- trueppm_api/apps/jiraimport/__init__.py +8 -0
- trueppm_api/apps/jiraimport/apps.py +11 -0
- trueppm_api/apps/jiraimport/migrations/0001_initial.py +72 -0
- trueppm_api/apps/jiraimport/migrations/__init__.py +0 -0
- trueppm_api/apps/jiraimport/models.py +70 -0
- trueppm_api/apps/jiraimport/parser.py +350 -0
- trueppm_api/apps/jiraimport/services.py +46 -0
- trueppm_api/apps/jiraimport/tasks.py +277 -0
- trueppm_api/apps/jiraimport/urls.py +15 -0
- trueppm_api/apps/jiraimport/views.py +153 -0
- trueppm_api/apps/msproject/__init__.py +0 -0
- trueppm_api/apps/msproject/apps.py +11 -0
- trueppm_api/apps/msproject/dataclasses.py +153 -0
- trueppm_api/apps/msproject/exporter.py +500 -0
- trueppm_api/apps/msproject/extended_attributes.py +77 -0
- trueppm_api/apps/msproject/importer.py +1095 -0
- trueppm_api/apps/msproject/migrations/0001_import_request.py +77 -0
- trueppm_api/apps/msproject/migrations/0001_squashed_0002_importrequest_creates_project.py +80 -0
- trueppm_api/apps/msproject/migrations/0002_importrequest_creates_project.py +15 -0
- trueppm_api/apps/msproject/migrations/__init__.py +0 -0
- trueppm_api/apps/msproject/models.py +81 -0
- trueppm_api/apps/msproject/parser.py +1231 -0
- trueppm_api/apps/msproject/serializers.py +136 -0
- trueppm_api/apps/msproject/services.py +58 -0
- trueppm_api/apps/msproject/tasks.py +436 -0
- trueppm_api/apps/msproject/urls.py +39 -0
- trueppm_api/apps/msproject/views.py +531 -0
- trueppm_api/apps/notifications/__init__.py +7 -0
- trueppm_api/apps/notifications/apps.py +15 -0
- trueppm_api/apps/notifications/backfill.py +79 -0
- trueppm_api/apps/notifications/categories.py +112 -0
- trueppm_api/apps/notifications/delivery_limits.py +258 -0
- trueppm_api/apps/notifications/digests.py +447 -0
- trueppm_api/apps/notifications/email_backend.py +330 -0
- trueppm_api/apps/notifications/email_health.py +128 -0
- trueppm_api/apps/notifications/migrations/0001_initial.py +207 -0
- trueppm_api/apps/notifications/migrations/0001_squashed_0006_notification_task.py +294 -0
- trueppm_api/apps/notifications/migrations/0002_project_notification_preference.py +67 -0
- trueppm_api/apps/notifications/migrations/0003_projectnotificationpreference_paused.py +17 -0
- trueppm_api/apps/notifications/migrations/0004_clean_unknown_matrix_keys.py +30 -0
- trueppm_api/apps/notifications/migrations/0005_notification_body_notification_event_type_and_more.py +27 -0
- trueppm_api/apps/notifications/migrations/0006_notification_task.py +25 -0
- trueppm_api/apps/notifications/migrations/0007_workspaceemailsettings.py +80 -0
- trueppm_api/apps/notifications/migrations/0008_notification_snoozed_until.py +17 -0
- trueppm_api/apps/notifications/migrations/0009_usernotificationsettings.py +43 -0
- trueppm_api/apps/notifications/migrations/0010_projectnotificationpreference_schema_version.py +20 -0
- trueppm_api/apps/notifications/migrations/0011_usernotificationsettings_digest_hour_and_more.py +96 -0
- trueppm_api/apps/notifications/migrations/__init__.py +0 -0
- trueppm_api/apps/notifications/models.py +1197 -0
- trueppm_api/apps/notifications/receivers.py +155 -0
- trueppm_api/apps/notifications/schema_migrations.py +56 -0
- trueppm_api/apps/notifications/serializers.py +961 -0
- trueppm_api/apps/notifications/services.py +1451 -0
- trueppm_api/apps/notifications/tasks.py +711 -0
- trueppm_api/apps/notifications/throttles.py +98 -0
- trueppm_api/apps/notifications/urls.py +97 -0
- trueppm_api/apps/notifications/views.py +904 -0
- trueppm_api/apps/observability/__init__.py +0 -0
- trueppm_api/apps/observability/apps.py +31 -0
- trueppm_api/apps/observability/logging.py +213 -0
- trueppm_api/apps/observability/migrations/0001_initial.py +35 -0
- trueppm_api/apps/observability/migrations/0001_squashed_0003_alter_purgerun_trigger.py +154 -0
- trueppm_api/apps/observability/migrations/0002_purgerun_retentionpolicy_retentionschedule.py +131 -0
- trueppm_api/apps/observability/migrations/0003_alter_purgerun_trigger.py +21 -0
- trueppm_api/apps/observability/migrations/0004_alter_retentionpolicy_key.py +28 -0
- trueppm_api/apps/observability/migrations/__init__.py +0 -0
- trueppm_api/apps/observability/models.py +141 -0
- trueppm_api/apps/observability/otel/__init__.py +45 -0
- trueppm_api/apps/observability/otel/attributes.py +247 -0
- trueppm_api/apps/observability/otel/export_health.py +533 -0
- trueppm_api/apps/observability/otel/instrumentation.py +351 -0
- trueppm_api/apps/observability/otel/metrics.py +551 -0
- trueppm_api/apps/observability/otel/provider.py +511 -0
- trueppm_api/apps/observability/otel/request_attributes.py +204 -0
- trueppm_api/apps/observability/purge_registry.py +124 -0
- trueppm_api/apps/observability/retention.py +139 -0
- trueppm_api/apps/observability/selectors.py +969 -0
- trueppm_api/apps/observability/serializers.py +142 -0
- trueppm_api/apps/observability/services.py +407 -0
- trueppm_api/apps/observability/tasks.py +266 -0
- trueppm_api/apps/observability/urls.py +27 -0
- trueppm_api/apps/observability/views.py +523 -0
- trueppm_api/apps/profiles/__init__.py +0 -0
- trueppm_api/apps/profiles/apps.py +15 -0
- trueppm_api/apps/profiles/constants.py +54 -0
- trueppm_api/apps/profiles/migrations/0001_initial.py +55 -0
- trueppm_api/apps/profiles/migrations/0001_squashed_0004_userprofile_role_context.py +125 -0
- trueppm_api/apps/profiles/migrations/0002_userprofile_hidden_views.py +17 -0
- trueppm_api/apps/profiles/migrations/0003_projectvisit.py +63 -0
- trueppm_api/apps/profiles/migrations/0004_userprofile_role_context.py +26 -0
- trueppm_api/apps/profiles/migrations/0005_userprofile_schedule_in_deliver.py +20 -0
- trueppm_api/apps/profiles/migrations/0006_userprofile_date_format_userprofile_timezone.py +36 -0
- trueppm_api/apps/profiles/migrations/0007_userpin.py +91 -0
- trueppm_api/apps/profiles/migrations/0008_remove_userprofile_schedule_in_deliver.py +71 -0
- trueppm_api/apps/profiles/migrations/0009_auth_user_email_upper_idx.py +81 -0
- trueppm_api/apps/profiles/migrations/__init__.py +0 -0
- trueppm_api/apps/profiles/models.py +276 -0
- trueppm_api/apps/profiles/serializers.py +199 -0
- trueppm_api/apps/profiles/services.py +466 -0
- trueppm_api/apps/profiles/views.py +124 -0
- trueppm_api/apps/projects/__init__.py +0 -0
- trueppm_api/apps/projects/actual_date_rules.py +163 -0
- trueppm_api/apps/projects/amend.py +133 -0
- trueppm_api/apps/projects/apps.py +38 -0
- trueppm_api/apps/projects/asset_feed.py +459 -0
- trueppm_api/apps/projects/asset_views.py +317 -0
- trueppm_api/apps/projects/attachment_policy.py +278 -0
- trueppm_api/apps/projects/authentication.py +449 -0
- trueppm_api/apps/projects/backfill.py +287 -0
- trueppm_api/apps/projects/backlog_services.py +324 -0
- trueppm_api/apps/projects/backlog_views.py +302 -0
- trueppm_api/apps/projects/batch_operation_views.py +264 -0
- trueppm_api/apps/projects/blocker_services.py +510 -0
- trueppm_api/apps/projects/board_activity.py +462 -0
- trueppm_api/apps/projects/board_activity_views.py +186 -0
- trueppm_api/apps/projects/board_lanes.py +134 -0
- trueppm_api/apps/projects/bulk_settings.py +194 -0
- trueppm_api/apps/projects/bundled_templates.py +215 -0
- trueppm_api/apps/projects/calendar_settings.py +180 -0
- trueppm_api/apps/projects/ceremony_views.py +244 -0
- trueppm_api/apps/projects/commit_moment.py +204 -0
- trueppm_api/apps/projects/config_notice.py +1325 -0
- trueppm_api/apps/projects/custom_field_values.py +266 -0
- trueppm_api/apps/projects/decisions_services.py +93 -0
- trueppm_api/apps/projects/decisions_views.py +94 -0
- trueppm_api/apps/projects/estimation_scale.py +126 -0
- trueppm_api/apps/projects/export_bundle.py +353 -0
- trueppm_api/apps/projects/fixtures/seeds/atlas-platform-launch.json +4256 -0
- trueppm_api/apps/projects/fixtures/seeds/aurora-mobile-app.json +2000 -0
- trueppm_api/apps/projects/fixtures/seeds/bayside-civic-center.json +1825 -0
- trueppm_api/apps/projects/fixtures/seeds/ga-launch.json +1905 -0
- trueppm_api/apps/projects/fixtures/seeds/helios-crm-replacement.json +1409 -0
- trueppm_api/apps/projects/guardrail_policy_source.py +269 -0
- trueppm_api/apps/projects/inbound_sync.py +555 -0
- trueppm_api/apps/projects/iteration_label.py +85 -0
- trueppm_api/apps/projects/lifecycle.py +191 -0
- trueppm_api/apps/projects/management/__init__.py +0 -0
- trueppm_api/apps/projects/management/commands/__init__.py +0 -0
- trueppm_api/apps/projects/management/commands/backfill_in_progress_status.py +92 -0
- trueppm_api/apps/projects/management/commands/create_demo_share_link.py +368 -0
- trueppm_api/apps/projects/management/commands/export_program.py +50 -0
- trueppm_api/apps/projects/management/commands/import_seed.py +165 -0
- trueppm_api/apps/projects/management/commands/load_sample_project.py +247 -0
- trueppm_api/apps/projects/management/commands/seed_capacity.py +405 -0
- trueppm_api/apps/projects/management/commands/seed_integration_fixtures.py +178 -0
- trueppm_api/apps/projects/management/commands/shift_sample_dates.py +120 -0
- trueppm_api/apps/projects/mcp_settings.py +305 -0
- trueppm_api/apps/projects/methodology.py +178 -0
- trueppm_api/apps/projects/migrations/0001_initial.py +208 -0
- trueppm_api/apps/projects/migrations/0001_squashed_0094_project_decisions_policy.py +5595 -0
- trueppm_api/apps/projects/migrations/0002_alter_uuid_pk_serialize_false.py +57 -0
- trueppm_api/apps/projects/migrations/0003_rename_task_project_index.py +25 -0
- trueppm_api/apps/projects/migrations/0004_soft_delete_and_dependency_versioned.py +70 -0
- trueppm_api/apps/projects/migrations/0005_task_assignee.py +33 -0
- trueppm_api/apps/projects/migrations/0006_task_utilization_index.py +31 -0
- trueppm_api/apps/projects/migrations/0007_task_is_milestone.py +28 -0
- trueppm_api/apps/projects/migrations/0008_baseline.py +119 -0
- trueppm_api/apps/projects/migrations/0009_historicaldependency_historicalproject_and_more.py +210 -0
- trueppm_api/apps/projects/migrations/0010_risk.py +229 -0
- trueppm_api/apps/projects/migrations/0011_task_status.py +27 -0
- trueppm_api/apps/projects/migrations/0012_task_status.py +27 -0
- trueppm_api/apps/projects/migrations/0013_task_planned_start.py +24 -0
- trueppm_api/apps/projects/migrations/0014_short_hex_ids.py +74 -0
- trueppm_api/apps/projects/migrations/0015_backfill_short_ids.py +48 -0
- trueppm_api/apps/projects/migrations/0016_task_actual_dates.py +42 -0
- trueppm_api/apps/projects/migrations/0017_boardcolumnconfig.py +43 -0
- trueppm_api/apps/projects/migrations/0018_estimation_governance.py +62 -0
- trueppm_api/apps/projects/migrations/0019_backfill_wbs_paths.py +28 -0
- trueppm_api/apps/projects/migrations/0020_task_status_5col.py +65 -0
- trueppm_api/apps/projects/migrations/0021_boardsavedview.py +64 -0
- trueppm_api/apps/projects/migrations/0022_task_status_changed_at_priority_rank.py +35 -0
- trueppm_api/apps/projects/migrations/0023_risk_framework_fields.py +96 -0
- trueppm_api/apps/projects/migrations/0024_riskcomment.py +52 -0
- trueppm_api/apps/projects/migrations/0025_sprint.py +352 -0
- trueppm_api/apps/projects/migrations/0026_project_methodology.py +30 -0
- trueppm_api/apps/projects/migrations/0027_sprint_retro.py +89 -0
- trueppm_api/apps/projects/migrations/0028_normalize_notes_field.py +42 -0
- trueppm_api/apps/projects/migrations/0029_complete_implies_full_progress.py +38 -0
- trueppm_api/apps/projects/migrations/0030_review_implies_full_progress.py +37 -0
- trueppm_api/apps/projects/migrations/0031_task_remaining_points.py +22 -0
- trueppm_api/apps/projects/migrations/0032_task_is_subtask_and_sprint_scope_change.py +90 -0
- trueppm_api/apps/projects/migrations/0033_task_my_work_index.py +38 -0
- trueppm_api/apps/projects/migrations/0034_inbound_task_sync.py +313 -0
- trueppm_api/apps/projects/migrations/0035_retro_versioned_and_suggested_assignee.py +173 -0
- trueppm_api/apps/projects/migrations/0036_program_entity_and_membership.py +157 -0
- trueppm_api/apps/projects/migrations/0037_sprint_capacity_points.py +20 -0
- trueppm_api/apps/projects/migrations/0038_taskcomment_commentreaction_commentacknowledgement_and_more.py +233 -0
- trueppm_api/apps/projects/migrations/0039_apitoken_and_more.py +88 -0
- trueppm_api/apps/projects/migrations/0040_program_general_fields.py +119 -0
- trueppm_api/apps/projects/migrations/0041_ceremony_template_and_phase_gate_config.py +210 -0
- trueppm_api/apps/projects/migrations/0041_project_general_fields.py +106 -0
- trueppm_api/apps/projects/migrations/0042_merge_20260522_0410.py +12 -0
- trueppm_api/apps/projects/migrations/0042_program_rollup_config.py +95 -0
- trueppm_api/apps/projects/migrations/0042_workflow_color_and_custom_fields.py +94 -0
- trueppm_api/apps/projects/migrations/0043_merge_20260522_1118.py +10 -0
- trueppm_api/apps/projects/migrations/0044_merge_20260522_rollup.py +10 -0
- trueppm_api/apps/projects/migrations/0044_project_archive_program_close.py +119 -0
- trueppm_api/apps/projects/migrations/0045_program_risk_policy.py +78 -0
- trueppm_api/apps/projects/migrations/0046_merge_20260523_2248.py +12 -0
- trueppm_api/apps/projects/migrations/0047_apitokenauditentry_program_and_more.py +55 -0
- trueppm_api/apps/projects/migrations/0048_historicalprogram_color_program_color.py +32 -0
- trueppm_api/apps/projects/migrations/0049_backlog_item.py +112 -0
- trueppm_api/apps/projects/migrations/0050_backlog_item_trgm_search.py +26 -0
- trueppm_api/apps/projects/migrations/0051_alter_calendar_working_days_alter_dependency_lag_and_more.py +144 -0
- trueppm_api/apps/projects/migrations/0052_recurring_tasks.py +278 -0
- trueppm_api/apps/projects/migrations/0053_dependency_dep_pred_serverver_idx_and_more.py +56 -0
- trueppm_api/apps/projects/migrations/0054_sprintscopechange_goal_impact_and_more.py +117 -0
- trueppm_api/apps/projects/migrations/0055_historicaltask_sprint_pending_and_more.py +51 -0
- trueppm_api/apps/projects/migrations/0056_historicalproject_prioritization_model_and_more.py +276 -0
- trueppm_api/apps/projects/migrations/0057_historicalsprint_binding_committed_snapshot_and_more.py +58 -0
- trueppm_api/apps/projects/migrations/0058_historicaltask_delivery_mode_and_more.py +72 -0
- trueppm_api/apps/projects/migrations/0059_forecastsnapshot.py +80 -0
- trueppm_api/apps/projects/migrations/0060_historicalprojectsignalprivacypolicy_and_more.py +90 -0
- trueppm_api/apps/projects/migrations/0061_baselinetask_story_points.py +17 -0
- trueppm_api/apps/projects/migrations/0062_project_is_sample.py +22 -0
- trueppm_api/apps/projects/migrations/0063_historicalsprint_wip_limit_sprint_wip_limit.py +22 -0
- trueppm_api/apps/projects/migrations/0064_sprinttaskoutcome.py +97 -0
- trueppm_api/apps/projects/migrations/0065_historicalsprint_goal_outcome_sprint_goal_outcome.py +32 -0
- trueppm_api/apps/projects/migrations/0066_historicalproject_lead_project_lead.py +38 -0
- trueppm_api/apps/projects/migrations/0067_sprint_exclude_from_velocity.py +22 -0
- trueppm_api/apps/projects/migrations/0068_project_recalculated_at.py +23 -0
- trueppm_api/apps/projects/migrations/0069_iteration_label.py +22 -0
- trueppm_api/apps/projects/migrations/0070_historicalprogram_iteration_label_and_more.py +32 -0
- trueppm_api/apps/projects/migrations/0071_backfill_iteration_label_inherit.py +35 -0
- trueppm_api/apps/projects/migrations/0072_pulseresponse_retroboarditem.py +129 -0
- trueppm_api/apps/projects/migrations/0073_sprinttaskoutcome_demo_ready.py +17 -0
- trueppm_api/apps/projects/migrations/0074_historicaltask_blocked_reason_task_blocked_reason.py +30 -0
- trueppm_api/apps/projects/migrations/0075_risk_decimal_short_id.py +42 -0
- trueppm_api/apps/projects/migrations/0076_sprinttaskoutcome_review_polish.py +45 -0
- trueppm_api/apps/projects/migrations/0077_task_blocker_fields.py +111 -0
- trueppm_api/apps/projects/migrations/0078_alter_risktask_unique_together_and_more.py +22 -0
- trueppm_api/apps/projects/migrations/0079_historicalproject_status_date_project_status_date.py +22 -0
- trueppm_api/apps/projects/migrations/0080_alter_historicaltask_type_alter_task_type.py +46 -0
- trueppm_api/apps/projects/migrations/0081_historicalprogram_allow_guests_and_more.py +52 -0
- trueppm_api/apps/projects/migrations/0082_project_last_sync_version.py +90 -0
- trueppm_api/apps/projects/migrations/0083_historicalprogram_mc_history_attribution_audience_and_more.py +108 -0
- trueppm_api/apps/projects/migrations/0084_tasknote.py +71 -0
- trueppm_api/apps/projects/migrations/0085_sprint_ix_sprint_velocity.py +33 -0
- trueppm_api/apps/projects/migrations/0086_board_card_search_trgm.py +33 -0
- trueppm_api/apps/projects/migrations/0087_historicalprogram_task_duration_change_percent_policy_and_more.py +155 -0
- trueppm_api/apps/projects/migrations/0088_historicalprogram_allowed_attachment_types_and_more.py +61 -0
- trueppm_api/apps/projects/migrations/0089_signalceilingraiseproposal_signalceilingraisevote_and_more.py +157 -0
- trueppm_api/apps/projects/migrations/0090_historicaltask_proj_histdate_index.py +44 -0
- trueppm_api/apps/projects/migrations/0091_project_board_cadence.py +39 -0
- trueppm_api/apps/projects/migrations/0092_program_target_date.py +22 -0
- trueppm_api/apps/projects/migrations/0093_cross_project_dependency_consent.py +58 -0
- trueppm_api/apps/projects/migrations/0094_project_decisions_policy.py +90 -0
- trueppm_api/apps/projects/migrations/0095_poker_session_vote.py +127 -0
- trueppm_api/apps/projects/migrations/0095_squashed_0097_release_0_3.py +225 -0
- trueppm_api/apps/projects/migrations/0096_crossprojectslipconflict.py +91 -0
- trueppm_api/apps/projects/migrations/0097_backlogitem_backlogitem_tags_gin_and_more.py +32 -0
- trueppm_api/apps/projects/migrations/0098_historicalproject_show_baselines_and_more.py +52 -0
- trueppm_api/apps/projects/migrations/0099_apitoken_scopes.py +48 -0
- trueppm_api/apps/projects/migrations/0100_task_dep_serverver_idx_concurrent.py +61 -0
- trueppm_api/apps/projects/migrations/0101_dependency_deleted_at_task_deleted_at.py +22 -0
- trueppm_api/apps/projects/migrations/0102_project_deleted_at_project_proj_isdel_deletedat_idx.py +25 -0
- trueppm_api/apps/projects/migrations/0103_taskdurationchangeevent_task_dur_evt_sprint_idx.py +18 -0
- trueppm_api/apps/projects/migrations/0104_historicalproject_stale_task_threshold_days_and_more.py +32 -0
- trueppm_api/apps/projects/migrations/0104_trash_restore_1113.py +38 -0
- trueppm_api/apps/projects/migrations/0105_historical_changelog_indexes.py +62 -0
- trueppm_api/apps/projects/migrations/0106_boardsavedview_schema_version.py +20 -0
- trueppm_api/apps/projects/migrations/0107_task_activity_event.py +70 -0
- trueppm_api/apps/projects/migrations/0108_remove_apitoken_api_token_scope_xor_and_more.py +117 -0
- trueppm_api/apps/projects/migrations/0109_projectexportjob.py +82 -0
- trueppm_api/apps/projects/migrations/0110_sharelink.py +123 -0
- trueppm_api/apps/projects/migrations/0111_projectcalendarlayer.py +67 -0
- trueppm_api/apps/projects/migrations/0112_sharelink_expires_at_alter_sharelink_content_kind.py +31 -0
- trueppm_api/apps/projects/migrations/0113_alter_historicaltask_percent_complete_and_more.py +35 -0
- trueppm_api/apps/projects/migrations/0114_historicalproject_default_member_role_and_more.py +28 -0
- trueppm_api/apps/projects/migrations/0115_historicalproject_end_date_shift_threshold_days_and_more.py +22 -0
- trueppm_api/apps/projects/migrations/0116_alter_taskactivityevent_event_type.py +28 -0
- trueppm_api/apps/projects/migrations/0117_label_tasklabel_label_tasks_and_more.py +126 -0
- trueppm_api/apps/projects/migrations/0118_programexportjob.py +82 -0
- trueppm_api/apps/projects/migrations/0119_historicalprogram_calendar_program_calendar.py +36 -0
- trueppm_api/apps/projects/migrations/0120_alter_backlogitem_item_type.py +29 -0
- trueppm_api/apps/projects/migrations/0121_task_three_point_estimate_ordered.py +64 -0
- trueppm_api/apps/projects/migrations/0122_task_relation.py +173 -0
- trueppm_api/apps/projects/migrations/0123_remove_historicalproject_agile_features_and_more.py +20 -0
- trueppm_api/apps/projects/migrations/0124_dependency_is_driving.py +17 -0
- trueppm_api/apps/projects/migrations/0125_historicalprogram_estimation_scale_and_more.py +68 -0
- trueppm_api/apps/projects/migrations/0126_projectcustomfield_show_on_card_taskcustomfieldvalue.py +80 -0
- trueppm_api/apps/projects/migrations/0127_historicalprogram_mcp_enabled_and_more.py +32 -0
- trueppm_api/apps/projects/migrations/0128_acceptancecriterion_sync_seq_apitoken_sync_seq_and_more.py +192 -0
- trueppm_api/apps/projects/migrations/0129_sharelink_show_milestone_dates.py +20 -0
- trueppm_api/apps/projects/migrations/0130_programimportjob.py +85 -0
- trueppm_api/apps/projects/migrations/0131_historicalprogram_sprint_picker_ready_only_default_and_more.py +32 -0
- trueppm_api/apps/projects/migrations/0132_task_scheduled_start.py +17 -0
- trueppm_api/apps/projects/migrations/0133_project_status_date_floor_armed_at.py +17 -0
- trueppm_api/apps/projects/migrations/0134_task_edited_at_task_seeded_at_task_source_id_and_more.py +65 -0
- trueppm_api/apps/projects/migrations/0135_projecttemplate_templateapplication_and_more.py +175 -0
- trueppm_api/apps/projects/migrations/0136_schedule_outline_batch_operations.py +118 -0
- trueppm_api/apps/projects/migrations/0137_collapse_dual_scope_api_tokens.py +54 -0
- trueppm_api/apps/projects/migrations/0138_apitokenauditentry_api_token_audit_owner_idx.py +18 -0
- trueppm_api/apps/projects/migrations/0139_historicaltask_duration_unit_task_duration_unit.py +32 -0
- trueppm_api/apps/projects/migrations/0140_historicaltask_auto_container_and_more.py +113 -0
- trueppm_api/apps/projects/migrations/0141_historicalproject_draft_started_at_and_more.py +44 -0
- trueppm_api/apps/projects/migrations/0142_baseline_calendar_hours_per_day_and_more.py +31 -0
- trueppm_api/apps/projects/migrations/0143_sprintcloserequest_failure_reason_and_more.py +42 -0
- trueppm_api/apps/projects/migrations/0144_projecttemplate_source_project_and_more.py +35 -0
- trueppm_api/apps/projects/migrations/0145_seed_bundled_templates.py +65 -0
- trueppm_api/apps/projects/migrations/0146_historicaltask_board_lane_task_board_lane.py +32 -0
- trueppm_api/apps/projects/migrations/0147_structuraloperation.py +128 -0
- trueppm_api/apps/projects/migrations/0148_task_unique_task_wbs_path_per_project_live.py +76 -0
- trueppm_api/apps/projects/migrations/0149_drop_workshops_tables.py +42 -0
- trueppm_api/apps/projects/migrations/0150_clear_uncommitted_cpm_output.py +130 -0
- trueppm_api/apps/projects/migrations/0151_config_notice_request.py +70 -0
- trueppm_api/apps/projects/migrations/0152_aggregate_task_wbs_name_idx.py +20 -0
- trueppm_api/apps/projects/migrations/0153_program_sample_anchor_date.py +22 -0
- trueppm_api/apps/projects/migrations/__init__.py +0 -0
- trueppm_api/apps/projects/models.py +8946 -0
- trueppm_api/apps/projects/poker_services.py +154 -0
- trueppm_api/apps/projects/poker_views.py +398 -0
- trueppm_api/apps/projects/product_backlog_services.py +450 -0
- trueppm_api/apps/projects/program_label_services.py +94 -0
- trueppm_api/apps/projects/program_label_views.py +189 -0
- trueppm_api/apps/projects/program_rollup.py +575 -0
- trueppm_api/apps/projects/program_schedule.py +745 -0
- trueppm_api/apps/projects/program_views.py +3220 -0
- trueppm_api/apps/projects/project_templates.py +422 -0
- trueppm_api/apps/projects/receivers.py +234 -0
- trueppm_api/apps/projects/refusal_codes.py +140 -0
- trueppm_api/apps/projects/reorder_services.py +145 -0
- trueppm_api/apps/projects/restructure_hooks.py +54 -0
- trueppm_api/apps/projects/retro_board_services.py +368 -0
- trueppm_api/apps/projects/retro_services.py +285 -0
- trueppm_api/apps/projects/risk_import.py +396 -0
- trueppm_api/apps/projects/row_identity.py +113 -0
- trueppm_api/apps/projects/schema_migrations.py +228 -0
- trueppm_api/apps/projects/schemas/seed_v1.json +493 -0
- trueppm_api/apps/projects/schemas/seed_v2.json +892 -0
- trueppm_api/apps/projects/seed/__init__.py +61 -0
- trueppm_api/apps/projects/seed/exporter.py +1644 -0
- trueppm_api/apps/projects/seed/forecast_backfill.py +247 -0
- trueppm_api/apps/projects/seed/importer.py +2390 -0
- trueppm_api/apps/projects/seed/reanchor.py +551 -0
- trueppm_api/apps/projects/seed/reldates.py +126 -0
- trueppm_api/apps/projects/seed/replace.py +126 -0
- trueppm_api/apps/projects/seed/replay.py +1691 -0
- trueppm_api/apps/projects/seed/replay_ctx.py +41 -0
- trueppm_api/apps/projects/seed/samples.py +427 -0
- trueppm_api/apps/projects/seed/validation.py +1149 -0
- trueppm_api/apps/projects/serializers.py +12310 -0
- trueppm_api/apps/projects/services.py +6966 -0
- trueppm_api/apps/projects/share_serializers.py +132 -0
- trueppm_api/apps/projects/share_services.py +325 -0
- trueppm_api/apps/projects/share_views.py +374 -0
- trueppm_api/apps/projects/sharing_settings.py +187 -0
- trueppm_api/apps/projects/signal_privacy_services.py +909 -0
- trueppm_api/apps/projects/signal_privacy_views.py +476 -0
- trueppm_api/apps/projects/signals.py +132 -0
- trueppm_api/apps/projects/slip_conflict.py +266 -0
- trueppm_api/apps/projects/sprint_cadence.py +453 -0
- trueppm_api/apps/projects/sprint_picker_settings.py +132 -0
- trueppm_api/apps/projects/standup.py +304 -0
- trueppm_api/apps/projects/standup_views.py +99 -0
- trueppm_api/apps/projects/structural_operation_services.py +727 -0
- trueppm_api/apps/projects/structural_operation_views.py +311 -0
- trueppm_api/apps/projects/surface_visibility.py +126 -0
- trueppm_api/apps/projects/task_batch_services.py +379 -0
- trueppm_api/apps/projects/task_bulk.py +1152 -0
- trueppm_api/apps/projects/task_classification.py +561 -0
- trueppm_api/apps/projects/task_classification_defaults.py +95 -0
- trueppm_api/apps/projects/task_duration_settings.py +142 -0
- trueppm_api/apps/projects/task_grouping.py +754 -0
- trueppm_api/apps/projects/tasks.py +1917 -0
- trueppm_api/apps/projects/template_divergence.py +168 -0
- trueppm_api/apps/projects/template_services.py +160 -0
- trueppm_api/apps/projects/template_tasks.py +258 -0
- trueppm_api/apps/projects/template_views.py +799 -0
- trueppm_api/apps/projects/throttles.py +311 -0
- trueppm_api/apps/projects/urls.py +1011 -0
- trueppm_api/apps/projects/utilization.py +885 -0
- trueppm_api/apps/projects/views.py +20572 -0
- trueppm_api/apps/projects/wbs_paths.py +119 -0
- trueppm_api/apps/resources/__init__.py +0 -0
- trueppm_api/apps/resources/apps.py +8 -0
- trueppm_api/apps/resources/capacity.py +121 -0
- trueppm_api/apps/resources/migrations/0001_initial.py +68 -0
- trueppm_api/apps/resources/migrations/0001_squashed_0013_alter_projectresource_unique_together_and_more.py +281 -0
- trueppm_api/apps/resources/migrations/0002_alter_uuid_pk_serialize_false.py +36 -0
- trueppm_api/apps/resources/migrations/0003_resource_soft_delete.py +30 -0
- trueppm_api/apps/resources/migrations/0004_add_taskresource_resource_index.py +25 -0
- trueppm_api/apps/resources/migrations/0005_resource_job_role.py +20 -0
- trueppm_api/apps/resources/migrations/0006_skill.py +34 -0
- trueppm_api/apps/resources/migrations/0007_resource_skill.py +63 -0
- trueppm_api/apps/resources/migrations/0008_project_resource.py +63 -0
- trueppm_api/apps/resources/migrations/0009_task_skill_requirement.py +60 -0
- trueppm_api/apps/resources/migrations/0010_alter_resourceskill_options_and_more.py +20 -0
- trueppm_api/apps/resources/migrations/0011_alter_projectresource_notes.py +17 -0
- trueppm_api/apps/resources/migrations/0012_add_resource_user_fk.py +26 -0
- trueppm_api/apps/resources/migrations/0013_alter_projectresource_unique_together_and_more.py +53 -0
- trueppm_api/apps/resources/migrations/0014_projectresource_sync_seq_resource_sync_seq_and_more.py +37 -0
- trueppm_api/apps/resources/migrations/0015_projectresource_deactivated_with_resource.py +17 -0
- trueppm_api/apps/resources/migrations/__init__.py +0 -0
- trueppm_api/apps/resources/models.py +298 -0
- trueppm_api/apps/resources/serializers.py +426 -0
- trueppm_api/apps/resources/services.py +465 -0
- trueppm_api/apps/resources/urls.py +28 -0
- trueppm_api/apps/resources/views.py +1749 -0
- trueppm_api/apps/scheduling/__init__.py +0 -0
- trueppm_api/apps/scheduling/apps.py +153 -0
- trueppm_api/apps/scheduling/calendars.py +119 -0
- trueppm_api/apps/scheduling/deadletter.py +91 -0
- trueppm_api/apps/scheduling/forecast_history_settings.py +215 -0
- trueppm_api/apps/scheduling/forecast_staleness.py +313 -0
- trueppm_api/apps/scheduling/graph_guard.py +263 -0
- trueppm_api/apps/scheduling/management/__init__.py +0 -0
- trueppm_api/apps/scheduling/management/commands/__init__.py +0 -0
- trueppm_api/apps/scheduling/management/commands/prune_forecast_snapshots.py +53 -0
- trueppm_api/apps/scheduling/migrations/0001_failed_task.py +62 -0
- trueppm_api/apps/scheduling/migrations/0001_squashed_0007_projectforecastsnapshot.py +309 -0
- trueppm_api/apps/scheduling/migrations/0002_schedulerequest.py +78 -0
- trueppm_api/apps/scheduling/migrations/0003_add_schedule_request_reason.py +27 -0
- trueppm_api/apps/scheduling/migrations/0004_velocity_suggestion.py +94 -0
- trueppm_api/apps/scheduling/migrations/0005_montecarlorun.py +61 -0
- trueppm_api/apps/scheduling/migrations/0006_montecarlorun_distribution.py +17 -0
- trueppm_api/apps/scheduling/migrations/0007_projectforecastsnapshot.py +65 -0
- trueppm_api/apps/scheduling/migrations/0008_alter_schedulerequest_reason.py +28 -0
- trueppm_api/apps/scheduling/migrations/0008_folded_schedulerequest_reason.py +34 -0
- trueppm_api/apps/scheduling/migrations/0009_alter_schedulerequest_reason.py +29 -0
- trueppm_api/apps/scheduling/migrations/0010_failedtask_resolution_note_failedtask_resolved_at_and_more.py +43 -0
- trueppm_api/apps/scheduling/migrations/0011_failedtask_project.py +26 -0
- trueppm_api/apps/scheduling/migrations/0012_montecarlorun_diagnostic.py +17 -0
- trueppm_api/apps/scheduling/migrations/0013_montecarlorun_status_date.py +17 -0
- trueppm_api/apps/scheduling/migrations/0014_montecarlorun_plan_version.py +17 -0
- trueppm_api/apps/scheduling/migrations/__init__.py +0 -0
- trueppm_api/apps/scheduling/models.py +516 -0
- trueppm_api/apps/scheduling/receivers.py +160 -0
- trueppm_api/apps/scheduling/risk_premium.py +283 -0
- trueppm_api/apps/scheduling/serializers.py +564 -0
- trueppm_api/apps/scheduling/services.py +1182 -0
- trueppm_api/apps/scheduling/signals.py +56 -0
- trueppm_api/apps/scheduling/tasks.py +1984 -0
- trueppm_api/apps/scheduling/telemetry.py +80 -0
- trueppm_api/apps/scheduling/units.py +35 -0
- trueppm_api/apps/scheduling/urls.py +57 -0
- trueppm_api/apps/scheduling/views.py +2564 -0
- trueppm_api/apps/sso/__init__.py +0 -0
- trueppm_api/apps/sso/apps.py +19 -0
- trueppm_api/apps/sso/extensions.py +130 -0
- trueppm_api/apps/sso/management/__init__.py +0 -0
- trueppm_api/apps/sso/management/commands/__init__.py +0 -0
- trueppm_api/apps/sso/management/commands/seed_sso_keycloak.py +172 -0
- trueppm_api/apps/sso/migrations/0001_initial.py +119 -0
- trueppm_api/apps/sso/migrations/0002_remove_oidcprovider_workspace_ssoproviderpolicy_and_more.py +169 -0
- trueppm_api/apps/sso/migrations/__init__.py +0 -0
- trueppm_api/apps/sso/models.py +148 -0
- trueppm_api/apps/sso/serializers.py +437 -0
- trueppm_api/apps/sso/services.py +1385 -0
- trueppm_api/apps/sso/urls.py +47 -0
- trueppm_api/apps/sso/views.py +935 -0
- trueppm_api/apps/sync/__init__.py +0 -0
- trueppm_api/apps/sync/apps.py +15 -0
- trueppm_api/apps/sync/backfill.py +208 -0
- trueppm_api/apps/sync/broadcast.py +320 -0
- trueppm_api/apps/sync/conflict.py +262 -0
- trueppm_api/apps/sync/consumers.py +471 -0
- trueppm_api/apps/sync/migrations/0001_initial.py +59 -0
- trueppm_api/apps/sync/migrations/0001_squashed_0002_remove_syncbatch_syncbatch_project_client_batch_uniq_and_more.py +77 -0
- trueppm_api/apps/sync/migrations/0002_remove_syncbatch_syncbatch_project_client_batch_uniq_and_more.py +41 -0
- trueppm_api/apps/sync/migrations/0003_boardevent.py +46 -0
- trueppm_api/apps/sync/migrations/0004_seed_sync_seq.py +24 -0
- trueppm_api/apps/sync/migrations/0005_program_sync_sequence.py +35 -0
- trueppm_api/apps/sync/migrations/__init__.py +0 -0
- trueppm_api/apps/sync/models.py +225 -0
- trueppm_api/apps/sync/pagination.py +231 -0
- trueppm_api/apps/sync/sequence.py +371 -0
- trueppm_api/apps/sync/serializers.py +687 -0
- trueppm_api/apps/sync/tasks.py +260 -0
- trueppm_api/apps/sync/throttles.py +175 -0
- trueppm_api/apps/sync/upload.py +501 -0
- trueppm_api/apps/sync/urls.py +17 -0
- trueppm_api/apps/sync/views.py +930 -0
- trueppm_api/apps/sync/ws_auth.py +193 -0
- trueppm_api/apps/taskruns/__init__.py +0 -0
- trueppm_api/apps/taskruns/apps.py +11 -0
- trueppm_api/apps/taskruns/migrations/0001_initial.py +92 -0
- trueppm_api/apps/taskruns/migrations/__init__.py +0 -0
- trueppm_api/apps/taskruns/models.py +68 -0
- trueppm_api/apps/taskruns/serializers.py +92 -0
- trueppm_api/apps/taskruns/tasks.py +60 -0
- trueppm_api/apps/taskruns/tracker.py +286 -0
- trueppm_api/apps/taskruns/urls.py +46 -0
- trueppm_api/apps/taskruns/views.py +195 -0
- trueppm_api/apps/teams/__init__.py +8 -0
- trueppm_api/apps/teams/apps.py +16 -0
- trueppm_api/apps/teams/migrations/0001_initial.py +139 -0
- trueppm_api/apps/teams/migrations/0001_squashed_0002_default_teams.py +142 -0
- trueppm_api/apps/teams/migrations/0002_default_teams.py +62 -0
- trueppm_api/apps/teams/migrations/0003_team_sync_seq_teammembership_sync_seq.py +22 -0
- trueppm_api/apps/teams/migrations/__init__.py +0 -0
- trueppm_api/apps/teams/models.py +145 -0
- trueppm_api/apps/teams/permissions.py +149 -0
- trueppm_api/apps/teams/serializers.py +95 -0
- trueppm_api/apps/teams/services.py +434 -0
- trueppm_api/apps/teams/signals.py +61 -0
- trueppm_api/apps/teams/urls.py +34 -0
- trueppm_api/apps/teams/views.py +201 -0
- trueppm_api/apps/timetracking/__init__.py +0 -0
- trueppm_api/apps/timetracking/apps.py +9 -0
- trueppm_api/apps/timetracking/migrations/0001_initial.py +117 -0
- trueppm_api/apps/timetracking/migrations/0002_timesheetsubmission.py +46 -0
- trueppm_api/apps/timetracking/migrations/0003_timeentry_deleted_at_timeentry_deleted_by.py +31 -0
- trueppm_api/apps/timetracking/migrations/0004_timeentry_sync_seq_and_more.py +24 -0
- trueppm_api/apps/timetracking/migrations/__init__.py +0 -0
- trueppm_api/apps/timetracking/models.py +178 -0
- trueppm_api/apps/timetracking/serializers.py +175 -0
- trueppm_api/apps/timetracking/services.py +117 -0
- trueppm_api/apps/timetracking/urls.py +47 -0
- trueppm_api/apps/timetracking/views.py +526 -0
- trueppm_api/apps/webhooks/__init__.py +0 -0
- trueppm_api/apps/webhooks/apps.py +8 -0
- trueppm_api/apps/webhooks/backfill.py +90 -0
- trueppm_api/apps/webhooks/dispatch.py +144 -0
- trueppm_api/apps/webhooks/migrations/0001_initial.py +146 -0
- trueppm_api/apps/webhooks/migrations/0001_squashed_0007_sprint_lifecycle_events.py +243 -0
- trueppm_api/apps/webhooks/migrations/0002_alter_webhook_events.py +32 -0
- trueppm_api/apps/webhooks/migrations/0003_delivery_status_created_idx.py +17 -0
- trueppm_api/apps/webhooks/migrations/0004_webhook_program_alter_webhook_project_and_more.py +64 -0
- trueppm_api/apps/webhooks/migrations/0005_webhook_delivery_sequence_numbers.py +30 -0
- trueppm_api/apps/webhooks/migrations/0006_webhook_format_alter_webhook_events_and_more.py +61 -0
- trueppm_api/apps/webhooks/migrations/0007_sprint_lifecycle_events.py +63 -0
- trueppm_api/apps/webhooks/migrations/0008_risk_baseline_comment_events.py +73 -0
- trueppm_api/apps/webhooks/migrations/0009_encrypt_webhook_secret_and_failure_counters.py +90 -0
- trueppm_api/apps/webhooks/migrations/__init__.py +0 -0
- trueppm_api/apps/webhooks/models.py +372 -0
- trueppm_api/apps/webhooks/serializers.py +318 -0
- trueppm_api/apps/webhooks/tasks.py +663 -0
- trueppm_api/apps/webhooks/urls.py +22 -0
- trueppm_api/apps/webhooks/views.py +383 -0
- trueppm_api/apps/workflow_engine/__init__.py +0 -0
- trueppm_api/apps/workflow_engine/apps.py +23 -0
- trueppm_api/apps/workflow_engine/migrations/0001_initial.py +230 -0
- trueppm_api/apps/workflow_engine/migrations/0001_squashed_0002_remove_workflowhistoryevent_workflow_history_lookup_idx.py +234 -0
- trueppm_api/apps/workflow_engine/migrations/0002_remove_workflowhistoryevent_workflow_history_lookup_idx.py +16 -0
- trueppm_api/apps/workflow_engine/migrations/0003_workflowoutboxrow_workflow_outbox_recovery_idx.py +21 -0
- trueppm_api/apps/workflow_engine/migrations/__init__.py +0 -0
- trueppm_api/apps/workflow_engine/models.py +265 -0
- trueppm_api/apps/workflow_engine/tasks.py +368 -0
- trueppm_api/apps/workspace/__init__.py +0 -0
- trueppm_api/apps/workspace/apps.py +10 -0
- trueppm_api/apps/workspace/backfill.py +111 -0
- trueppm_api/apps/workspace/export.py +242 -0
- trueppm_api/apps/workspace/migrations/0001_initial.py +326 -0
- trueppm_api/apps/workspace/migrations/0001_squashed_0014_workspacemembership_availability_effective_from_and_more.py +829 -0
- trueppm_api/apps/workspace/migrations/0002_remove_workspace_fiscal_year_start_and_more.py +85 -0
- trueppm_api/apps/workspace/migrations/0003_workspaceexportjob.py +73 -0
- trueppm_api/apps/workspace/migrations/0004_alter_workspaceinvite_status.py +27 -0
- trueppm_api/apps/workspace/migrations/0005_workspace_iteration_label_and_more.py +26 -0
- trueppm_api/apps/workspace/migrations/0006_alter_groupmembership_unique_together_and_more.py +45 -0
- trueppm_api/apps/workspace/migrations/0007_workspace_public_sharing_override_policy.py +21 -0
- trueppm_api/apps/workspace/migrations/0008_workspace_mc_history_attribution_audience_and_more.py +44 -0
- trueppm_api/apps/workspace/migrations/0009_workspace_methodology_and_more.py +186 -0
- trueppm_api/apps/workspace/migrations/0010_workspace_logo.py +38 -0
- trueppm_api/apps/workspace/migrations/0011_historicalworkspace_task_duration_change_percent_override_policy_and_more.py +56 -0
- trueppm_api/apps/workspace/migrations/0012_historicalworkspace_allowed_attachment_types_and_more.py +64 -0
- trueppm_api/apps/workspace/migrations/0013_auditevent.py +70 -0
- trueppm_api/apps/workspace/migrations/0014_workspacemembership_availability_effective_from_and_more.py +39 -0
- trueppm_api/apps/workspace/migrations/0015_trash_restore_1113.py +34 -0
- trueppm_api/apps/workspace/migrations/0016_historicalworkspace_calendar_and_more.py +55 -0
- trueppm_api/apps/workspace/migrations/0017_historicalworkspace_estimation_scale_and_more.py +38 -0
- trueppm_api/apps/workspace/migrations/0018_historicalworkspace_feedback_enabled_and_more.py +32 -0
- trueppm_api/apps/workspace/migrations/0019_historicalworkspace_mcp_enabled_and_more.py +22 -0
- trueppm_api/apps/workspace/migrations/0020_viewer_ordinal_one.py +34 -0
- trueppm_api/apps/workspace/migrations/0021_group_sync_seq_groupmembership_sync_seq_and_more.py +27 -0
- trueppm_api/apps/workspace/migrations/0022_historicalworkspace_sprint_picker_ready_only_default_and_more.py +22 -0
- trueppm_api/apps/workspace/migrations/0023_alter_auditevent_event_type.py +32 -0
- trueppm_api/apps/workspace/migrations/0024_alter_auditevent_event_type.py +33 -0
- trueppm_api/apps/workspace/migrations/0025_alter_auditevent_event_type.py +36 -0
- trueppm_api/apps/workspace/migrations/0026_alter_auditevent_event_type.py +37 -0
- trueppm_api/apps/workspace/migrations/0027_alter_auditevent_event_type.py +42 -0
- trueppm_api/apps/workspace/migrations/__init__.py +0 -0
- trueppm_api/apps/workspace/models.py +857 -0
- trueppm_api/apps/workspace/permissions.py +187 -0
- trueppm_api/apps/workspace/serializers.py +590 -0
- trueppm_api/apps/workspace/services.py +919 -0
- trueppm_api/apps/workspace/signals.py +37 -0
- trueppm_api/apps/workspace/tasks.py +555 -0
- trueppm_api/apps/workspace/urls.py +113 -0
- trueppm_api/apps/workspace/views.py +1502 -0
- trueppm_api/asgi.py +42 -0
- trueppm_api/celery.py +31 -0
- trueppm_api/core/__init__.py +0 -0
- trueppm_api/core/admin_site.py +206 -0
- trueppm_api/core/auth_views.py +916 -0
- trueppm_api/core/constant_time.py +51 -0
- trueppm_api/core/csp.py +54 -0
- trueppm_api/core/db.py +87 -0
- trueppm_api/core/db_session.py +37 -0
- trueppm_api/core/exception_handlers.py +157 -0
- trueppm_api/core/export_downloads.py +56 -0
- trueppm_api/core/extension_providers.py +56 -0
- trueppm_api/core/extension_signals.py +147 -0
- trueppm_api/core/idempotent.py +260 -0
- trueppm_api/core/metadata.py +42 -0
- trueppm_api/core/middleware.py +51 -0
- trueppm_api/core/openapi.py +949 -0
- trueppm_api/core/password_reset.py +500 -0
- trueppm_api/core/protect_conflict.py +100 -0
- trueppm_api/core/ratelimit.py +148 -0
- trueppm_api/core/redis_throttle.py +82 -0
- trueppm_api/core/refresh_cookie_policy.py +156 -0
- trueppm_api/core/request_body.py +86 -0
- trueppm_api/core/security_checks.py +884 -0
- trueppm_api/core/storage_config.py +105 -0
- trueppm_api/core/text_decode.py +168 -0
- trueppm_api/core/throttling.py +344 -0
- trueppm_api/core/valkey.py +214 -0
- trueppm_api/core/valkey_cache.py +66 -0
- trueppm_api/core/valkey_checks.py +104 -0
- trueppm_api/core/valkey_config.py +39 -0
- trueppm_api/core/worker_broker_wait.py +149 -0
- trueppm_api/core/worker_heartbeat.py +134 -0
- trueppm_api/fields.py +36 -0
- trueppm_api/permissions.py +30 -0
- trueppm_api/py.typed +0 -0
- trueppm_api/routing.py +11 -0
- trueppm_api/settings/__init__.py +0 -0
- trueppm_api/settings/base.py +2455 -0
- trueppm_api/settings/dev.py +273 -0
- trueppm_api/settings/prod.py +228 -0
- trueppm_api/urls.py +153 -0
- trueppm_api/workflows/__init__.py +38 -0
- trueppm_api/workflows/backends/__init__.py +6 -0
- trueppm_api/workflows/backends/default.py +297 -0
- trueppm_api/workflows/consumers/__init__.py +11 -0
- trueppm_api/workflows/consumers/requeue_failed_task.py +65 -0
- trueppm_api/workflows/interface.py +148 -0
- trueppm_api/workflows/registry.py +106 -0
- trueppm_api/workflows/services.py +90 -0
- trueppm_api/wsgi.py +11 -0
- trueppm_api-0.4.0b2.dist-info/METADATA +70 -0
- trueppm_api-0.4.0b2.dist-info/RECORD +759 -0
- trueppm_api-0.4.0b2.dist-info/WHEEL +4 -0
|
@@ -0,0 +1,397 @@
|
|
|
1
|
+
"""Agent-action recording service (ADR-0112 RC1, #1805).
|
|
2
|
+
|
|
3
|
+
``record_agent_action`` is the single writer of the hash chain. It allocates the next
|
|
4
|
+
per-instance ``sequence``, links to the predecessor's ``record_hash``, writes the
|
|
5
|
+
append-only ``AgentAction`` row, and advances the chain head — all under one
|
|
6
|
+
``select_for_update`` on the singleton ``AgentActionChainHead`` so two concurrent
|
|
7
|
+
callers cannot interleave the chain (ADR-0112's mechanism (a): the lock also protects
|
|
8
|
+
the hash linkage, which a bare DB sequence would not). The ``agent_action_recorded``
|
|
9
|
+
signal is dispatched in ``on_commit`` so it fires only once the row and its link are
|
|
10
|
+
durable.
|
|
11
|
+
|
|
12
|
+
The write is **fail-closed**: it runs inside the request's atomic block, so if the
|
|
13
|
+
append fails the whole request rolls back rather than serving an un-audited read — an
|
|
14
|
+
audit substrate must never return a read it could not record.
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
from __future__ import annotations
|
|
18
|
+
|
|
19
|
+
import hashlib
|
|
20
|
+
import ipaddress
|
|
21
|
+
import json
|
|
22
|
+
from dataclasses import dataclass
|
|
23
|
+
from datetime import timedelta
|
|
24
|
+
from typing import TYPE_CHECKING, Any
|
|
25
|
+
|
|
26
|
+
from django.db import transaction
|
|
27
|
+
from django.utils import timezone
|
|
28
|
+
|
|
29
|
+
from trueppm_api.apps.agents.canonical import canonical_fields, compute_record_hash
|
|
30
|
+
from trueppm_api.apps.agents.models import (
|
|
31
|
+
SAMPLE_SUMMARY_PREFIX,
|
|
32
|
+
SAMPLE_TOKEN_PREFIX,
|
|
33
|
+
AgentAction,
|
|
34
|
+
AgentActionChainHead,
|
|
35
|
+
AgentActionCheckpoint,
|
|
36
|
+
AgentActionRefusalDetail,
|
|
37
|
+
AgentActionVerdict,
|
|
38
|
+
AgentActorKind,
|
|
39
|
+
)
|
|
40
|
+
from trueppm_api.apps.agents.signals import agent_action_prune_requested, agent_action_recorded
|
|
41
|
+
from trueppm_api.core.extension_signals import dispatch_extension_signal
|
|
42
|
+
|
|
43
|
+
if TYPE_CHECKING:
|
|
44
|
+
from datetime import datetime
|
|
45
|
+
|
|
46
|
+
from rest_framework.request import Request
|
|
47
|
+
|
|
48
|
+
from trueppm_api.apps.projects.models import ProjectApiToken
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def _safe_ip(value: str | None) -> str | None:
|
|
52
|
+
"""Return ``value`` only if it parses as a valid IP address, else ``None``.
|
|
53
|
+
|
|
54
|
+
Guards the fail-closed save: a forgeable ``X-Forwarded-For`` (which may carry a
|
|
55
|
+
hostname or garbage) must never raise on ``GenericIPAddressField`` and turn a valid
|
|
56
|
+
read into a 500.
|
|
57
|
+
"""
|
|
58
|
+
|
|
59
|
+
if not value:
|
|
60
|
+
return None
|
|
61
|
+
try:
|
|
62
|
+
ipaddress.ip_address(value)
|
|
63
|
+
except ValueError:
|
|
64
|
+
return None
|
|
65
|
+
return value
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def engine_version() -> str:
|
|
69
|
+
"""The trueppm-scheduler version at decision time — the answer-stamp engine anchor.
|
|
70
|
+
|
|
71
|
+
Resolved from the installed package metadata (never drifts from the wheel). Falls
|
|
72
|
+
back to ``"unknown"`` so a metadata hiccup can never break the audited request.
|
|
73
|
+
"""
|
|
74
|
+
|
|
75
|
+
try:
|
|
76
|
+
from trueppm_scheduler import __version__
|
|
77
|
+
|
|
78
|
+
return str(__version__)
|
|
79
|
+
except Exception:
|
|
80
|
+
return "unknown"
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def hash_request_payload(request: Request) -> str:
|
|
84
|
+
"""sha256 over the canonicalized request payload — the per-request reproducibility hash.
|
|
85
|
+
|
|
86
|
+
Hashes the method, path, sorted query parameters, and the request body when present
|
|
87
|
+
(a read usually has none). Never includes headers, so no token material is hashed.
|
|
88
|
+
|
|
89
|
+
Total by construction, and that has to be enforced rather than assumed. A malformed
|
|
90
|
+
body is hashed as its raw bytes. Reading ``request.body`` *after* something has
|
|
91
|
+
consumed the stream — which every DRF view that touches ``request.data`` on a
|
|
92
|
+
POST/PATCH has done by the time an audit runs — raises ``RawPostDataException``, and
|
|
93
|
+
``getattr(..., default)`` does not swallow it (the default only covers
|
|
94
|
+
``AttributeError``). Such a body is recorded as unavailable rather than raised: this
|
|
95
|
+
function runs on the audit path, where raising converts a clean refusal into a 500
|
|
96
|
+
(#3017).
|
|
97
|
+
"""
|
|
98
|
+
|
|
99
|
+
parts: dict[str, Any] = {
|
|
100
|
+
"method": request.method,
|
|
101
|
+
"path": request.path,
|
|
102
|
+
"query": sorted(request.GET.items()),
|
|
103
|
+
}
|
|
104
|
+
try:
|
|
105
|
+
body = getattr(request, "body", b"") or b""
|
|
106
|
+
except Exception:
|
|
107
|
+
parts["body_sha256"] = "unavailable"
|
|
108
|
+
body = b""
|
|
109
|
+
if body:
|
|
110
|
+
parts["body_sha256"] = hashlib.sha256(body).hexdigest()
|
|
111
|
+
canonical = json.dumps(parts, sort_keys=True, separators=(",", ":"), default=str)
|
|
112
|
+
return hashlib.sha256(canonical.encode()).hexdigest()
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def _attribution(
|
|
116
|
+
actor_token: ProjectApiToken | None, summary: str, *, sample: bool
|
|
117
|
+
) -> tuple[str, str]:
|
|
118
|
+
"""Return the hashed ``(actor_token_prefix, summary)`` pair for a new chain row.
|
|
119
|
+
|
|
120
|
+
A sample row replaces both with the demo markers, because the marker has to live
|
|
121
|
+
in hashed fields (see :func:`record_agent_action`).
|
|
122
|
+
"""
|
|
123
|
+
if sample:
|
|
124
|
+
return SAMPLE_TOKEN_PREFIX, f"{SAMPLE_SUMMARY_PREFIX}{summary or ''}"
|
|
125
|
+
return (actor_token.token_prefix if actor_token is not None else ""), summary
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def _record_refusal_detail(
|
|
129
|
+
entry: AgentAction,
|
|
130
|
+
verdict: str,
|
|
131
|
+
refusal_constraint: str,
|
|
132
|
+
projected_impact: dict[str, Any] | None,
|
|
133
|
+
) -> None:
|
|
134
|
+
"""Create the non-hashed refusal side-car for a refusal that names a constraint."""
|
|
135
|
+
if verdict == AgentActionVerdict.REFUSED and refusal_constraint:
|
|
136
|
+
AgentActionRefusalDetail.objects.create(
|
|
137
|
+
action=entry,
|
|
138
|
+
constraint=refusal_constraint,
|
|
139
|
+
projected_impact=projected_impact or {},
|
|
140
|
+
)
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
def record_agent_action(
|
|
144
|
+
*,
|
|
145
|
+
actor_kind: str = AgentActorKind.MCP_TOKEN,
|
|
146
|
+
actor_token: ProjectApiToken | None,
|
|
147
|
+
principal: Any | None,
|
|
148
|
+
action: str,
|
|
149
|
+
method: str,
|
|
150
|
+
capability_used: str,
|
|
151
|
+
verdict: str,
|
|
152
|
+
payload_hash: str,
|
|
153
|
+
refusal_reason: str = "",
|
|
154
|
+
refusal_constraint: str = "",
|
|
155
|
+
projected_impact: dict[str, Any] | None = None,
|
|
156
|
+
object_type: str = "",
|
|
157
|
+
object_id: str = "",
|
|
158
|
+
project_id: Any | None = None,
|
|
159
|
+
summary: str = "",
|
|
160
|
+
source_ip: str | None = None,
|
|
161
|
+
engine_version_str: str | None = None,
|
|
162
|
+
occurred_at: datetime | None = None,
|
|
163
|
+
sample: bool = False,
|
|
164
|
+
) -> AgentAction:
|
|
165
|
+
"""Append one hash-chained ``AgentAction`` row and advance the chain head.
|
|
166
|
+
|
|
167
|
+
``sample=True`` is for the bundled-sample loader only (#3603). The row still
|
|
168
|
+
goes through the real chain — the agent-oversight panel reads nothing else —
|
|
169
|
+
but is marked as demo data in **hashed** fields: ``actor_token_prefix`` is
|
|
170
|
+
:data:`SAMPLE_TOKEN_PREFIX` and ``summary`` leads with
|
|
171
|
+
:data:`SAMPLE_SUMMARY_PREFIX`. The marker must live in the hash because the
|
|
172
|
+
row outlives its project (a sample reload SET_NULLs ``project``), and a marker
|
|
173
|
+
anyone could strip would let fabricated history pass as evidence; stripping
|
|
174
|
+
this one breaks ``audit_verify``. Sample rows also skip the
|
|
175
|
+
``agent_action_recorded`` extension signal, so nothing downstream notarizes
|
|
176
|
+
them.
|
|
177
|
+
|
|
178
|
+
Serializes on the singleton chain head so the ``sequence`` is gap-free and each row
|
|
179
|
+
links to the true predecessor. Must be called inside a DB transaction (the request's
|
|
180
|
+
ATOMIC_REQUESTS block, or an explicit ``transaction.atomic``); the ``on_commit``
|
|
181
|
+
signal fires on the outermost commit.
|
|
182
|
+
|
|
183
|
+
When ``verdict`` is a refusal and ``refusal_constraint`` is given, a non-hashed
|
|
184
|
+
:class:`AgentActionRefusalDetail` side-car is created **in the same atomic block**
|
|
185
|
+
(ADR-0421, #1850) so the constraint that fired and the projected impact commit
|
|
186
|
+
together with the chain row — the two can never diverge. ``projected_impact`` is
|
|
187
|
+
``{}`` for the current MCP-scope/identity producers (no schedule impact); the 0.6
|
|
188
|
+
gated-write producers populate it. The side-car is telemetry only; it is not part of
|
|
189
|
+
the hashed record, so it never affects ``audit_verify``.
|
|
190
|
+
"""
|
|
191
|
+
|
|
192
|
+
if verdict != AgentActionVerdict.REFUSED:
|
|
193
|
+
# refusal_reason/constraint are meaningful only for a refusal; never persist a
|
|
194
|
+
# dangling one (a non-refusal must not carry a refusal side-car either).
|
|
195
|
+
refusal_reason = ""
|
|
196
|
+
refusal_constraint = ""
|
|
197
|
+
|
|
198
|
+
occurred = occurred_at or timezone.now()
|
|
199
|
+
token_prefix, summary = _attribution(actor_token, summary, sample=sample)
|
|
200
|
+
resolved_engine = engine_version_str or engine_version()
|
|
201
|
+
# Sanitize the client IP: a spoofed/malformed X-Forwarded-For must not raise on the
|
|
202
|
+
# GenericIPAddressField save and 500 an otherwise-valid (fail-closed) read.
|
|
203
|
+
safe_ip = _safe_ip(source_ip)
|
|
204
|
+
|
|
205
|
+
with transaction.atomic():
|
|
206
|
+
# Lock the chain head for the duration of the append so the predecessor link and
|
|
207
|
+
# the sequence allocation are atomic against any concurrent recorder.
|
|
208
|
+
head = AgentActionChainHead.objects.select_for_update().get(pk=1)
|
|
209
|
+
sequence = head.last_sequence + 1
|
|
210
|
+
prev_hash = head.last_record_hash
|
|
211
|
+
|
|
212
|
+
entry = AgentAction(
|
|
213
|
+
schema_version=AgentAction._meta.get_field("schema_version").default,
|
|
214
|
+
actor_kind=actor_kind,
|
|
215
|
+
actor_token=actor_token,
|
|
216
|
+
actor_token_prefix=token_prefix,
|
|
217
|
+
principal=principal if getattr(principal, "pk", None) else None,
|
|
218
|
+
action=action,
|
|
219
|
+
method=(method or "")[:8],
|
|
220
|
+
object_type=object_type or "",
|
|
221
|
+
object_id=str(object_id or "")[:64],
|
|
222
|
+
project_id=project_id,
|
|
223
|
+
capability_used=capability_used or "",
|
|
224
|
+
verdict=verdict,
|
|
225
|
+
refusal_reason=refusal_reason,
|
|
226
|
+
payload_hash=payload_hash,
|
|
227
|
+
engine_version=resolved_engine,
|
|
228
|
+
sequence=sequence,
|
|
229
|
+
prev_hash=prev_hash,
|
|
230
|
+
summary=summary or "",
|
|
231
|
+
source_ip=safe_ip,
|
|
232
|
+
occurred_at=occurred,
|
|
233
|
+
)
|
|
234
|
+
entry.record_hash = compute_record_hash(prev_hash, canonical_fields(entry))
|
|
235
|
+
entry.save(force_insert=True)
|
|
236
|
+
|
|
237
|
+
head.last_sequence = sequence
|
|
238
|
+
head.last_record_hash = entry.record_hash
|
|
239
|
+
head.save(update_fields=["last_sequence", "last_record_hash", "updated_at"])
|
|
240
|
+
|
|
241
|
+
# Non-hashed telemetry side-car (ADR-0421): same atomic block as the chain
|
|
242
|
+
# append so the detail and its row commit or roll back together. Only for a
|
|
243
|
+
# refusal that names a constraint — an allowed read never carries one.
|
|
244
|
+
_record_refusal_detail(entry, verdict, refusal_constraint, projected_impact)
|
|
245
|
+
|
|
246
|
+
if not sample:
|
|
247
|
+
transaction.on_commit(
|
|
248
|
+
lambda: dispatch_extension_signal(
|
|
249
|
+
agent_action_recorded, sender=AgentAction, action=entry
|
|
250
|
+
)
|
|
251
|
+
)
|
|
252
|
+
return entry
|
|
253
|
+
|
|
254
|
+
|
|
255
|
+
@dataclass(frozen=True)
|
|
256
|
+
class PruneResult:
|
|
257
|
+
"""Outcome of a :func:`prune_agent_actions` call (dry-run or committed).
|
|
258
|
+
|
|
259
|
+
``eligible`` is the number of rows the window selects; ``deleted`` is how many were
|
|
260
|
+
actually removed (0 on a dry-run). ``cutoff_sequence`` / ``cutoff_hash`` are the
|
|
261
|
+
prune floor — the highest sequence removed and its ``record_hash`` (the anchor a
|
|
262
|
+
``checkpoint`` re-seeds ``audit_verify`` from). All ``None`` when nothing was eligible.
|
|
263
|
+
"""
|
|
264
|
+
|
|
265
|
+
eligible: int
|
|
266
|
+
deleted: int
|
|
267
|
+
committed: bool
|
|
268
|
+
cutoff_sequence: int | None
|
|
269
|
+
cutoff_hash: str | None
|
|
270
|
+
first_retained_sequence: int | None
|
|
271
|
+
checkpoint_id: str | None
|
|
272
|
+
|
|
273
|
+
|
|
274
|
+
def _resolve_cutoff(
|
|
275
|
+
head: AgentActionChainHead,
|
|
276
|
+
*,
|
|
277
|
+
before: datetime | None,
|
|
278
|
+
keep_days: int | None,
|
|
279
|
+
keep_last: int | None,
|
|
280
|
+
) -> int:
|
|
281
|
+
"""Resolve a window into a **prefix** cutoff sequence (delete rows ``<=`` the result).
|
|
282
|
+
|
|
283
|
+
Count-based (``keep_last``) keeps the newest K by sequence. Age-based
|
|
284
|
+
(``before`` / ``keep_days``) deletes the longest contiguous prefix whose rows are
|
|
285
|
+
**all** older than the cutoff — it stops at the first (lowest-sequence) row that is not
|
|
286
|
+
old enough, so a row younger than the cutoff is never deleted and the surviving chain
|
|
287
|
+
can never gain a mid-chain gap even if ``occurred_at`` is slightly out of sequence order.
|
|
288
|
+
"""
|
|
289
|
+
|
|
290
|
+
if keep_last is not None:
|
|
291
|
+
# Newest K rows by sequence survive; everything at or below the floor is pruned.
|
|
292
|
+
return head.last_sequence - keep_last
|
|
293
|
+
|
|
294
|
+
cutoff_at = timezone.now() - timedelta(days=keep_days) if keep_days is not None else before
|
|
295
|
+
first_not_old = (
|
|
296
|
+
AgentAction.objects.filter(occurred_at__gte=cutoff_at).order_by("sequence").first()
|
|
297
|
+
)
|
|
298
|
+
if first_not_old is None:
|
|
299
|
+
# Every row is older than the cutoff — the prefix is the whole chain.
|
|
300
|
+
return head.last_sequence
|
|
301
|
+
return first_not_old.sequence - 1
|
|
302
|
+
|
|
303
|
+
|
|
304
|
+
def prune_agent_actions(
|
|
305
|
+
*,
|
|
306
|
+
before: datetime | None = None,
|
|
307
|
+
keep_days: int | None = None,
|
|
308
|
+
keep_last: int | None = None,
|
|
309
|
+
through_sequence: int | None = None,
|
|
310
|
+
commit: bool = False,
|
|
311
|
+
actor: Any | None = None,
|
|
312
|
+
) -> PruneResult:
|
|
313
|
+
"""Chain-aware prune of the oldest ``AgentAction`` rows (ADR-0361, OSS admin-triggered).
|
|
314
|
+
|
|
315
|
+
Deletes a contiguous **prefix** (lowest sequences) selected by exactly one window and,
|
|
316
|
+
when it actually removes rows, writes an immutable :class:`AgentActionCheckpoint` so
|
|
317
|
+
``audit_verify`` re-anchors from the last-deleted row's ``record_hash`` instead of
|
|
318
|
+
genesis. Runs under the chain-head ``select_for_update`` so it serializes against
|
|
319
|
+
``record_agent_action`` and concurrent prunes. With ``commit=False`` (the default) it is
|
|
320
|
+
a dry-run: it reports the eligible count and resulting anchor without deleting or
|
|
321
|
+
checkpointing. A receiver of ``agent_action_prune_requested`` may raise to veto the whole
|
|
322
|
+
prune (the Enterprise legal-hold seam) — fail-closed inside the atomic block.
|
|
323
|
+
|
|
324
|
+
``through_sequence`` pins the cutoff to an explicit sequence instead of resolving a
|
|
325
|
+
window. The command uses it to commit exactly the prefix it previewed, so rows appended
|
|
326
|
+
between the dry-run and the ``--commit`` cannot enlarge a relative (``keep_last`` /
|
|
327
|
+
``keep_days``) cutoff and delete more than the operator confirmed.
|
|
328
|
+
"""
|
|
329
|
+
|
|
330
|
+
given = [v for v in (before, keep_days, keep_last, through_sequence) if v is not None]
|
|
331
|
+
if len(given) != 1:
|
|
332
|
+
raise ValueError("Provide exactly one of before, keep_days, keep_last, through_sequence.")
|
|
333
|
+
if keep_last is not None and keep_last < 0:
|
|
334
|
+
raise ValueError("keep_last must be non-negative.")
|
|
335
|
+
if keep_days is not None and keep_days < 0:
|
|
336
|
+
raise ValueError("keep_days must be non-negative.")
|
|
337
|
+
|
|
338
|
+
with transaction.atomic():
|
|
339
|
+
# Same lock the writer takes: serializes prune vs. append and prune vs. prune, and
|
|
340
|
+
# keeps the rows at/below the cutoff stable (appends only add rows above the head).
|
|
341
|
+
head = AgentActionChainHead.objects.select_for_update().get(pk=1)
|
|
342
|
+
cutoff_sequence = (
|
|
343
|
+
through_sequence
|
|
344
|
+
if through_sequence is not None
|
|
345
|
+
else _resolve_cutoff(head, before=before, keep_days=keep_days, keep_last=keep_last)
|
|
346
|
+
)
|
|
347
|
+
|
|
348
|
+
qs = AgentAction.objects.filter(sequence__lte=cutoff_sequence)
|
|
349
|
+
eligible = qs.count()
|
|
350
|
+
if eligible == 0:
|
|
351
|
+
return PruneResult(0, 0, False, None, None, None, None)
|
|
352
|
+
|
|
353
|
+
# The last-deleted row's record_hash is the anchor the surviving tail links to.
|
|
354
|
+
cutoff_row = AgentAction.objects.get(sequence=cutoff_sequence)
|
|
355
|
+
cutoff_hash = cutoff_row.record_hash
|
|
356
|
+
first_retained = cutoff_sequence + 1
|
|
357
|
+
|
|
358
|
+
if not commit:
|
|
359
|
+
return PruneResult(
|
|
360
|
+
eligible, 0, False, cutoff_sequence, cutoff_hash, first_retained, None
|
|
361
|
+
)
|
|
362
|
+
|
|
363
|
+
# Legal-hold veto point — a receiver raising here rolls back the whole prune.
|
|
364
|
+
#
|
|
365
|
+
# FAIL-CLOSED: `.send()` is correct here and must NOT become
|
|
366
|
+
# dispatch_extension_signal / send_robust. Every other OSS→Enterprise
|
|
367
|
+
# signal is robust so a third-party bug cannot break an OSS write path
|
|
368
|
+
# (#2606); this one is the inverse — the raise IS the mechanism. Making
|
|
369
|
+
# it robust would swallow a hold that failed to register and let the
|
|
370
|
+
# prune delete records under legal hold, inverting the safety property.
|
|
371
|
+
# scripts/check-extension-signals.sh recognizes the FAIL-CLOSED marker
|
|
372
|
+
# above and allows this site; do not remove it.
|
|
373
|
+
agent_action_prune_requested.send(
|
|
374
|
+
sender=AgentAction,
|
|
375
|
+
cutoff_sequence=cutoff_sequence,
|
|
376
|
+
cutoff_at=timezone.now(),
|
|
377
|
+
actor=actor,
|
|
378
|
+
)
|
|
379
|
+
|
|
380
|
+
deleted, _ = qs.delete()
|
|
381
|
+
checkpoint = AgentActionCheckpoint.objects.create(
|
|
382
|
+
pruned_through_sequence=cutoff_sequence,
|
|
383
|
+
pruned_through_hash=cutoff_hash,
|
|
384
|
+
first_retained_sequence=first_retained,
|
|
385
|
+
pruned_count=deleted,
|
|
386
|
+
pruned_by=actor if getattr(actor, "pk", None) else None,
|
|
387
|
+
created_at=timezone.now(),
|
|
388
|
+
)
|
|
389
|
+
return PruneResult(
|
|
390
|
+
eligible,
|
|
391
|
+
deleted,
|
|
392
|
+
True,
|
|
393
|
+
cutoff_sequence,
|
|
394
|
+
cutoff_hash,
|
|
395
|
+
first_retained,
|
|
396
|
+
str(checkpoint.id),
|
|
397
|
+
)
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"""The OSS agent-action extension-point signal (ADR-0112 §1.3).
|
|
2
|
+
|
|
3
|
+
OSS dispatches ``agent_action_recorded`` inside the recording transaction's
|
|
4
|
+
``on_commit`` — so it fires exactly when (and only when) the action durably committed
|
|
5
|
+
and its chain link is written. In the community edition nothing is registered and the
|
|
6
|
+
signal dispatches to nobody; OSS runs unchanged. Enterprise registers a receiver at
|
|
7
|
+
app-ready to append to its immutable/notarized org trail, feed the approval-workflow
|
|
8
|
+
engine, and evaluate org capability policy (#146–#148). Enterprise reads the frozen
|
|
9
|
+
event; it never reaches into OSS internals.
|
|
10
|
+
|
|
11
|
+
The signal is kept dependency-free (a bare ``Signal``) so importing it never drags in
|
|
12
|
+
models — the receiver side lives entirely in the ``trueppm-enterprise`` repo.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
from __future__ import annotations
|
|
16
|
+
|
|
17
|
+
from django.dispatch import Signal
|
|
18
|
+
|
|
19
|
+
#: Dispatched with ``sender=AgentAction`` and ``action=<AgentAction instance>`` in the
|
|
20
|
+
#: committing transaction's ``on_commit``. Additive-only kwargs (ADR-0112 contract).
|
|
21
|
+
agent_action_recorded = Signal()
|
|
22
|
+
|
|
23
|
+
#: The OSS legal-hold seam for chain-aware pruning (ADR-0361). Dispatched with plain
|
|
24
|
+
#: ``.send()`` **inside** the prune transaction, **before** the DELETE, carrying
|
|
25
|
+
#: ``cutoff_sequence``, ``cutoff_at``, and ``actor``. Any receiver that raises aborts the
|
|
26
|
+
#: whole atomic prune (fail-closed) — so Enterprise registers a legal-hold guard that
|
|
27
|
+
#: raises to block truncation of held rows, without OSS importing enterprise or forking
|
|
28
|
+
#: the purge. Community edition registers nobody and prunes unchanged. Additive-only
|
|
29
|
+
#: kwargs (ADR-0112 contract).
|
|
30
|
+
agent_action_prune_requested = Signal()
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"""URL patterns for the agents app — the team-readable agent-action log (#1805)."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from django.urls import path
|
|
6
|
+
|
|
7
|
+
from trueppm_api.apps.agents.views import AgentActionViewSet
|
|
8
|
+
|
|
9
|
+
agent_action_list = AgentActionViewSet.as_view({"get": "list"})
|
|
10
|
+
agent_action_detail = AgentActionViewSet.as_view({"get": "retrieve"})
|
|
11
|
+
|
|
12
|
+
urlpatterns = [
|
|
13
|
+
path("agent-actions/", agent_action_list, name="agent-action-list"),
|
|
14
|
+
path("agent-actions/<uuid:pk>/", agent_action_detail, name="agent-action-detail"),
|
|
15
|
+
]
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
"""Team-readable agent-action log endpoint (ADR-0112 §1.3, #1805).
|
|
2
|
+
|
|
3
|
+
``GET /api/v1/agent-actions/`` lists what the team's agents did — scoped so a caller sees
|
|
4
|
+
only actions in projects they are a member of, plus their own (actions where they are the
|
|
5
|
+
human principal). This is the OSS "team-readable" surface; the *immutable, notarized,
|
|
6
|
+
org-wide* trail is Enterprise (#146). The endpoint is read-only: the rows are append-only
|
|
7
|
+
and written only by ``record_agent_action``.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
import uuid
|
|
13
|
+
from typing import TYPE_CHECKING
|
|
14
|
+
|
|
15
|
+
from django.db.models import Q
|
|
16
|
+
from django.utils.dateparse import parse_datetime
|
|
17
|
+
from rest_framework import permissions, viewsets
|
|
18
|
+
|
|
19
|
+
from trueppm_api.apps.access.models import ProjectMembership
|
|
20
|
+
from trueppm_api.apps.agents.models import AgentAction
|
|
21
|
+
from trueppm_api.apps.agents.serializers import AgentActionSerializer
|
|
22
|
+
from trueppm_api.apps.projects.models import Project
|
|
23
|
+
|
|
24
|
+
if TYPE_CHECKING:
|
|
25
|
+
from django.db.models import QuerySet
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class AgentActionViewSet(viewsets.ReadOnlyModelViewSet): # type: ignore[type-arg]
|
|
29
|
+
"""Read-only, membership-scoped list/retrieve of audited agent actions.
|
|
30
|
+
|
|
31
|
+
Ordering is by ``sequence`` (chain order); filtering by ``verdict``, ``project``,
|
|
32
|
+
``program``, ``since``, and ``constraint`` is supported for the common "show me
|
|
33
|
+
refusals" / "show me this project" / "show me this program's agents (last 7d)" /
|
|
34
|
+
"show me every graph_validation refusal" (ADR-0421, #1850) triage queries. The
|
|
35
|
+
``program`` filter (#2020) powers the per-program agent-oversight panel: a program
|
|
36
|
+
groups projects, so a program's agent log is the union of the chain across the
|
|
37
|
+
program's member projects — always intersected with the caller's own membership
|
|
38
|
+
scope below, so the filter narrows what the caller may already see and never widens it.
|
|
39
|
+
"""
|
|
40
|
+
|
|
41
|
+
serializer_class = AgentActionSerializer
|
|
42
|
+
permission_classes = [permissions.IsAuthenticated]
|
|
43
|
+
|
|
44
|
+
def get_queryset(self) -> QuerySet[AgentAction]:
|
|
45
|
+
user = self.request.user
|
|
46
|
+
if not user.is_authenticated:
|
|
47
|
+
return AgentAction.objects.none()
|
|
48
|
+
|
|
49
|
+
member_project_ids = ProjectMembership.objects.filter(
|
|
50
|
+
user=user,
|
|
51
|
+
is_deleted=False,
|
|
52
|
+
).values_list("project_id", flat=True)
|
|
53
|
+
|
|
54
|
+
# A caller sees actions in their projects, plus their own agent's actions (which
|
|
55
|
+
# may target a project they no longer belong to, or none at all). The serializer
|
|
56
|
+
# renders project/principal as their FK ids (already on the row) and only the
|
|
57
|
+
# token *prefix*; ``refusal_detail`` is the one reverse OneToOne it reads, so
|
|
58
|
+
# select_related it to avoid an N+1 across the page (ADR-0421, #1850).
|
|
59
|
+
qs = AgentAction.objects.filter(
|
|
60
|
+
Q(project_id__in=member_project_ids) | Q(principal=user)
|
|
61
|
+
).select_related("refusal_detail")
|
|
62
|
+
|
|
63
|
+
verdict = self.request.query_params.get("verdict")
|
|
64
|
+
if verdict:
|
|
65
|
+
qs = qs.filter(verdict=verdict)
|
|
66
|
+
constraint = self.request.query_params.get("constraint")
|
|
67
|
+
if constraint:
|
|
68
|
+
# Filters through the side-car; non-refusals (no detail) drop out naturally.
|
|
69
|
+
qs = qs.filter(refusal_detail__constraint=constraint)
|
|
70
|
+
project_id = self.request.query_params.get("project")
|
|
71
|
+
if project_id:
|
|
72
|
+
# Validate the UUID here so a malformed ?project= returns an empty list rather
|
|
73
|
+
# than raising a 500 at queryset evaluation.
|
|
74
|
+
try:
|
|
75
|
+
uuid.UUID(str(project_id))
|
|
76
|
+
except ValueError:
|
|
77
|
+
return AgentAction.objects.none()
|
|
78
|
+
qs = qs.filter(project_id=project_id)
|
|
79
|
+
|
|
80
|
+
program_id = self.request.query_params.get("program")
|
|
81
|
+
if program_id:
|
|
82
|
+
try:
|
|
83
|
+
uuid.UUID(str(program_id))
|
|
84
|
+
except ValueError:
|
|
85
|
+
return AgentAction.objects.none()
|
|
86
|
+
# Resolve the program's project ids and filter with project_id__in so the query
|
|
87
|
+
# rides the existing (project, -occurred_at) index rather than joining through
|
|
88
|
+
# Project on every row (perf: one indexed IN vs. a per-row join). The result is
|
|
89
|
+
# still bounded by the membership/principal scope set above — a non-member gains
|
|
90
|
+
# nothing by naming a program they cannot see.
|
|
91
|
+
program_project_ids = Project.objects.filter(program_id=program_id).values_list(
|
|
92
|
+
"id", flat=True
|
|
93
|
+
)
|
|
94
|
+
qs = qs.filter(project_id__in=program_project_ids)
|
|
95
|
+
|
|
96
|
+
since = self.request.query_params.get("since")
|
|
97
|
+
if since:
|
|
98
|
+
# A malformed ?since= is ignored (the range simply doesn't narrow) rather than
|
|
99
|
+
# 500ing or emptying the list — a lenient time-window filter, unlike the strict
|
|
100
|
+
# UUID filters above where a bad id can only be a client bug.
|
|
101
|
+
parsed_since = parse_datetime(since)
|
|
102
|
+
if parsed_since is not None:
|
|
103
|
+
qs = qs.filter(occurred_at__gte=parsed_since)
|
|
104
|
+
|
|
105
|
+
# Order by -occurred_at (the leading key of the (project|principal, -occurred_at)
|
|
106
|
+
# indexes so the membership/principal filter and the sort share an index), with
|
|
107
|
+
# -sequence as a strict, deterministic tiebreak for same-instant rows.
|
|
108
|
+
return qs.order_by("-occurred_at", "-sequence")
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"""CSV / Excel spreadsheet import (#743, ADR-0632).
|
|
2
|
+
|
|
3
|
+
Parses a `.csv` or `.xlsx` upload into the shared ``ProjectData`` interchange
|
|
4
|
+
dataclass and hands it to ``msproject.importer.import_project`` — the same
|
|
5
|
+
persistence path MS Project and Jira already use (ADR-0259). This app owns a
|
|
6
|
+
parser, a fuzzy column mapper, and its own transactional outbox; it writes no
|
|
7
|
+
task, dependency, or resource rows itself.
|
|
8
|
+
|
|
9
|
+
The spreadsheet is the single largest source of new users: most small and
|
|
10
|
+
mid-size teams run their schedule in Excel and cannot evaluate TruePPM without
|
|
11
|
+
a migration path that is not manual re-entry.
|
|
12
|
+
"""
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"""Django app config for CSV / Excel import."""
|
|
2
|
+
|
|
3
|
+
from django.apps import AppConfig
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class CsvImportConfig(AppConfig):
|
|
7
|
+
"""CSV / Excel spreadsheet import app."""
|
|
8
|
+
|
|
9
|
+
name = "trueppm_api.apps.csvimport"
|
|
10
|
+
verbose_name = "CSV Import"
|
|
11
|
+
default_auto_field = "django.db.models.BigAutoField"
|