project-agent 0.2.2__tar.gz → 0.2.5__tar.gz
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.
- {project_agent-0.2.2 → project_agent-0.2.5}/PKG-INFO +1 -1
- {project_agent-0.2.2 → project_agent-0.2.5}/pyproject.toml +3 -1
- project_agent-0.2.5/src/project_agent/application/bootstrap/apply.py +222 -0
- {project_agent-0.2.2 → project_agent-0.2.5}/src/project_agent/application/bootstrap/checkpoint.py +20 -29
- project_agent-0.2.5/src/project_agent/application/bootstrap/contracts.py +80 -0
- {project_agent-0.2.2 → project_agent-0.2.5}/src/project_agent/application/bootstrap/init.py +12 -12
- {project_agent-0.2.2 → project_agent-0.2.5}/src/project_agent/application/bootstrap/loop_state.py +1 -1
- project_agent-0.2.5/src/project_agent/application/context/add_project_member.py +50 -0
- project_agent-0.2.5/src/project_agent/application/context/get_project_context.py +184 -0
- {project_agent-0.2.2 → project_agent-0.2.5}/src/project_agent/application/context/get_user_context.py +1 -0
- project_agent-0.2.5/src/project_agent/application/context/list_core_relationships.py +34 -0
- {project_agent-0.2.2 → project_agent-0.2.5}/src/project_agent/application/context/project_summary.py +109 -2
- {project_agent-0.2.2 → project_agent-0.2.5}/src/project_agent/application/context/resolve_user.py +25 -2
- {project_agent-0.2.2 → project_agent-0.2.5}/src/project_agent/application/context/search_user.py +1 -0
- project_agent-0.2.5/src/project_agent/application/context/set_project_owner.py +227 -0
- {project_agent-0.2.2 → project_agent-0.2.5}/src/project_agent/application/context/upsert_user_identity.py +4 -0
- project_agent-0.2.5/src/project_agent/application/context/workstreams.py +248 -0
- {project_agent-0.2.2 → project_agent-0.2.5}/src/project_agent/application/doc/render.py +491 -244
- {project_agent-0.2.2 → project_agent-0.2.5}/src/project_agent/application/doc/sync.py +45 -11
- {project_agent-0.2.2 → project_agent-0.2.5}/src/project_agent/application/doc/writeback.py +14 -11
- {project_agent-0.2.2 → project_agent-0.2.5}/src/project_agent/application/doc_hub/manage.py +162 -21
- {project_agent-0.2.2 → project_agent-0.2.5}/src/project_agent/application/doc_hub/read.py +60 -17
- {project_agent-0.2.2 → project_agent-0.2.5}/src/project_agent/application/loop/enqueue.py +8 -4
- {project_agent-0.2.2 → project_agent-0.2.5}/src/project_agent/application/loop/run.py +56 -24
- project_agent-0.2.2/src/project_agent/application/mind/create_version.py → project_agent-0.2.5/src/project_agent/application/mind/create_sprint.py +77 -58
- {project_agent-0.2.2 → project_agent-0.2.5}/src/project_agent/application/mind/delete_task.py +61 -29
- project_agent-0.2.2/src/project_agent/application/mind/finalize_version_closure.py → project_agent-0.2.5/src/project_agent/application/mind/finalize_sprint_closure.py +126 -95
- {project_agent-0.2.2 → project_agent-0.2.5}/src/project_agent/application/mind/milestones.py +3 -3
- project_agent-0.2.5/src/project_agent/application/mind/planned_sprints.py +29 -0
- {project_agent-0.2.2 → project_agent-0.2.5}/src/project_agent/application/mind/propose_change.py +262 -91
- project_agent-0.2.5/src/project_agent/application/mind/read.py +531 -0
- {project_agent-0.2.2 → project_agent-0.2.5}/src/project_agent/application/mind/set_tracking.py +22 -4
- project_agent-0.2.5/src/project_agent/application/mind/signoff_proposal.py +558 -0
- {project_agent-0.2.2 → project_agent-0.2.5}/src/project_agent/application/mind/snooze_task_reminder.py +12 -12
- project_agent-0.2.2/src/project_agent/application/mind/switch_version.py → project_agent-0.2.5/src/project_agent/application/mind/switch_sprint.py +49 -30
- project_agent-0.2.5/src/project_agent/application/mind/update_project.py +159 -0
- project_agent-0.2.5/src/project_agent/application/mind/update_sprint.py +300 -0
- project_agent-0.2.5/src/project_agent/application/mind/write_task_update.py +767 -0
- {project_agent-0.2.2 → project_agent-0.2.5}/src/project_agent/application/monitoring/evaluate_followups.py +61 -10
- {project_agent-0.2.2 → project_agent-0.2.5}/src/project_agent/application/monitoring/run.py +9 -0
- project_agent-0.2.5/src/project_agent/application/onboarding/__init__.py +1 -0
- project_agent-0.2.5/src/project_agent/application/onboarding/apply.py +307 -0
- project_agent-0.2.5/src/project_agent/application/onboarding/contracts.py +41 -0
- project_agent-0.2.5/src/project_agent/application/proposals/__init__.py +1 -0
- project_agent-0.2.5/src/project_agent/application/proposals/apply.py +358 -0
- project_agent-0.2.5/src/project_agent/application/proposals/contracts.py +53 -0
- project_agent-0.2.5/src/project_agent/application/query/__init__.py +4 -0
- project_agent-0.2.5/src/project_agent/application/query/apply.py +803 -0
- project_agent-0.2.5/src/project_agent/application/query/contracts.py +73 -0
- project_agent-0.2.5/src/project_agent/application/report/apply.py +106 -0
- {project_agent-0.2.2 → project_agent-0.2.5}/src/project_agent/application/report/build.py +526 -232
- project_agent-0.2.5/src/project_agent/application/report/contracts.py +38 -0
- project_agent-0.2.5/src/project_agent/application/scheduled/run.py +984 -0
- project_agent-0.2.5/src/project_agent/application/shared/actor.py +86 -0
- project_agent-0.2.5/src/project_agent/application/shared/core_relationship_read.py +338 -0
- project_agent-0.2.5/src/project_agent/application/shared/project_membership.py +20 -0
- project_agent-0.2.5/src/project_agent/application/shared/project_owner_revision.py +53 -0
- project_agent-0.2.5/src/project_agent/application/shared/project_participants.py +123 -0
- project_agent-0.2.5/src/project_agent/application/shared/project_reference.py +125 -0
- project_agent-0.2.5/src/project_agent/application/shared/scene_command.py +49 -0
- project_agent-0.2.5/src/project_agent/application/shared/user_refs.py +105 -0
- project_agent-0.2.5/src/project_agent/application/shared/workstream_revision.py +54 -0
- project_agent-0.2.5/src/project_agent/application/shared/workstreams.py +23 -0
- project_agent-0.2.5/src/project_agent/application/sprint_closure/__init__.py +7 -0
- project_agent-0.2.5/src/project_agent/application/sprint_closure/apply.py +437 -0
- project_agent-0.2.5/src/project_agent/application/sprint_closure/contracts.py +39 -0
- project_agent-0.2.5/src/project_agent/application/sprint_closure/evaluate.py +112 -0
- project_agent-0.2.5/src/project_agent/application/sprints/__init__.py +4 -0
- project_agent-0.2.5/src/project_agent/application/sprints/apply.py +821 -0
- project_agent-0.2.5/src/project_agent/application/sprints/contracts.py +164 -0
- project_agent-0.2.5/src/project_agent/application/tasks/__init__.py +5 -0
- project_agent-0.2.5/src/project_agent/application/tasks/apply.py +1381 -0
- project_agent-0.2.5/src/project_agent/application/tasks/backend.py +109 -0
- project_agent-0.2.5/src/project_agent/application/tasks/composite_write.py +111 -0
- project_agent-0.2.5/src/project_agent/application/tasks/contracts.py +264 -0
- project_agent-0.2.5/src/project_agent/application/tasks/migrate.py +74 -0
- {project_agent-0.2.2 → project_agent-0.2.5}/src/project_agent/domain/bootstrap/models.py +8 -7
- project_agent-0.2.5/src/project_agent/domain/core_relationships.py +155 -0
- {project_agent-0.2.2 → project_agent-0.2.5}/src/project_agent/domain/doc/models.py +10 -7
- {project_agent-0.2.2 → project_agent-0.2.5}/src/project_agent/domain/doc_hub/models.py +57 -4
- project_agent-0.2.5/src/project_agent/domain/mind/approval.py +342 -0
- project_agent-0.2.5/src/project_agent/domain/mind/models.py +379 -0
- {project_agent-0.2.2 → project_agent-0.2.5}/src/project_agent/domain/monitoring/evaluator.py +2 -2
- {project_agent-0.2.2 → project_agent-0.2.5}/src/project_agent/domain/monitoring/models.py +69 -26
- {project_agent-0.2.2 → project_agent-0.2.5}/src/project_agent/domain/project_context/models.py +105 -33
- {project_agent-0.2.2 → project_agent-0.2.5}/src/project_agent/domain/report/models.py +95 -85
- {project_agent-0.2.2 → project_agent-0.2.5}/src/project_agent/domain/scheduled/models.py +13 -4
- {project_agent-0.2.2 → project_agent-0.2.5}/src/project_agent/domain/shared/documents.py +12 -1
- project_agent-0.2.5/src/project_agent/domain/shared/project_mind_derived.py +145 -0
- {project_agent-0.2.2 → project_agent-0.2.5}/src/project_agent/domain/shared/project_mind_readiness.py +9 -9
- project_agent-0.2.5/src/project_agent/domain/shared/project_participants.py +14 -0
- project_agent-0.2.5/src/project_agent/domain/shared/sprints.py +10 -0
- {project_agent-0.2.2 → project_agent-0.2.5}/src/project_agent/domain/shared/statuses.py +31 -4
- project_agent-0.2.5/src/project_agent/domain/shared/user_refs.py +31 -0
- project_agent-0.2.5/src/project_agent/domain/shared/workstreams.py +56 -0
- project_agent-0.2.5/src/project_agent/domain/sprint_closure/__init__.py +13 -0
- {project_agent-0.2.2/src/project_agent/domain/version_closure → project_agent-0.2.5/src/project_agent/domain/sprint_closure}/evaluator.py +93 -74
- {project_agent-0.2.2/src/project_agent/domain/version_closure → project_agent-0.2.5/src/project_agent/domain/sprint_closure}/models.py +42 -29
- project_agent-0.2.5/src/project_agent/domain/tasks/__init__.py +1 -0
- project_agent-0.2.5/src/project_agent/domain/tasks/rules.py +42 -0
- {project_agent-0.2.2 → project_agent-0.2.5}/src/project_agent/domain/user/models.py +4 -17
- project_agent-0.2.5/src/project_agent/infrastructure/bootstrap/__init__.py +1 -0
- project_agent-0.2.5/src/project_agent/infrastructure/bootstrap/apply_runtime.py +102 -0
- {project_agent-0.2.2 → project_agent-0.2.5}/src/project_agent/infrastructure/clock/workday_clock.py +15 -0
- project_agent-0.2.5/src/project_agent/infrastructure/db/channel_rename.py +120 -0
- project_agent-0.2.5/src/project_agent/infrastructure/db/core_relationships.py +1815 -0
- {project_agent-0.2.2 → project_agent-0.2.5}/src/project_agent/infrastructure/db/repositories.py +672 -95
- project_agent-0.2.5/src/project_agent/infrastructure/db/schema.py +1313 -0
- {project_agent-0.2.2 → project_agent-0.2.5}/src/project_agent/infrastructure/db/tables.py +160 -18
- project_agent-0.2.5/src/project_agent/infrastructure/feature_flags.py +18 -0
- project_agent-0.2.5/src/project_agent/infrastructure/onboarding/__init__.py +1 -0
- project_agent-0.2.5/src/project_agent/infrastructure/onboarding/apply_runtime.py +127 -0
- project_agent-0.2.5/src/project_agent/infrastructure/proposals/__init__.py +1 -0
- project_agent-0.2.5/src/project_agent/infrastructure/proposals/apply_runtime.py +498 -0
- project_agent-0.2.5/src/project_agent/infrastructure/reports/__init__.py +1 -0
- project_agent-0.2.5/src/project_agent/infrastructure/reports/apply_runtime.py +84 -0
- project_agent-0.2.5/src/project_agent/infrastructure/sprints/__init__.py +0 -0
- project_agent-0.2.5/src/project_agent/infrastructure/sprints/apply_runtime.py +603 -0
- project_agent-0.2.5/src/project_agent/infrastructure/sprints/rollover_runtime.py +222 -0
- project_agent-0.2.5/src/project_agent/infrastructure/task_center/__init__.py +1 -0
- project_agent-0.2.5/src/project_agent/infrastructure/task_center/client.py +466 -0
- project_agent-0.2.5/src/project_agent/infrastructure/task_center/mapping.py +329 -0
- project_agent-0.2.5/src/project_agent/infrastructure/tasks/__init__.py +1 -0
- project_agent-0.2.5/src/project_agent/infrastructure/tasks/apply_runtime.py +720 -0
- project_agent-0.2.5/src/project_agent/infrastructure/tasks/backends.py +1759 -0
- project_agent-0.2.5/src/project_agent/infrastructure/tasks/factory.py +132 -0
- project_agent-0.2.5/src/project_agent/infrastructure/tasks/migration.py +52 -0
- project_agent-0.2.5/src/project_agent/infrastructure/tasks/task_group_project.py +134 -0
- {project_agent-0.2.2 → project_agent-0.2.5}/src/project_agent/interface/cli/app.py +16 -8
- project_agent-0.2.5/src/project_agent/interface/cli/commands/_monitoring_support.py +6 -0
- {project_agent-0.2.2 → project_agent-0.2.5}/src/project_agent/interface/cli/commands/bootstrap.py +25 -0
- {project_agent-0.2.2 → project_agent-0.2.5}/src/project_agent/interface/cli/commands/context.py +174 -295
- project_agent-0.2.5/src/project_agent/interface/cli/commands/db.py +186 -0
- {project_agent-0.2.2 → project_agent-0.2.5}/src/project_agent/interface/cli/commands/doc.py +81 -27
- {project_agent-0.2.2 → project_agent-0.2.5}/src/project_agent/interface/cli/commands/doc_hub.py +114 -18
- project_agent-0.2.5/src/project_agent/interface/cli/commands/mind.py +321 -0
- {project_agent-0.2.2 → project_agent-0.2.5}/src/project_agent/interface/cli/commands/monitoring.py +17 -10
- project_agent-0.2.5/src/project_agent/interface/cli/commands/onboarding.py +35 -0
- project_agent-0.2.5/src/project_agent/interface/cli/commands/proposal.py +33 -0
- project_agent-0.2.5/src/project_agent/interface/cli/commands/query.py +59 -0
- {project_agent-0.2.2 → project_agent-0.2.5}/src/project_agent/interface/cli/commands/report.py +41 -9
- {project_agent-0.2.2 → project_agent-0.2.5}/src/project_agent/interface/cli/commands/scheduled.py +26 -7
- project_agent-0.2.5/src/project_agent/interface/cli/commands/sprint.py +52 -0
- project_agent-0.2.5/src/project_agent/interface/cli/commands/task.py +32 -0
- {project_agent-0.2.2 → project_agent-0.2.5}/src/project_agent/interface/cli/helptext.py +27 -13
- project_agent-0.2.5/src/project_agent/interface/cli/scene_transport.py +49 -0
- project_agent-0.2.5/src/project_agent/interface/cli/unauthorized.py +35 -0
- {project_agent-0.2.2 → project_agent-0.2.5}/src/project_agent.egg-info/PKG-INFO +1 -1
- {project_agent-0.2.2 → project_agent-0.2.5}/src/project_agent.egg-info/SOURCES.txt +119 -28
- project_agent-0.2.5/tests/test_actor_ref.py +35 -0
- project_agent-0.2.5/tests/test_bootstrap_apply.py +200 -0
- {project_agent-0.2.2 → project_agent-0.2.5}/tests/test_bootstrap_checkpoint.py +246 -92
- project_agent-0.2.5/tests/test_cli_help.py +300 -0
- project_agent-0.2.2/tests/test_cli_version.py → project_agent-0.2.5/tests/test_cli_sprint.py +17 -0
- project_agent-0.2.5/tests/test_context_add_project_member.py +217 -0
- project_agent-0.2.5/tests/test_context_project_binding.py +154 -0
- project_agent-0.2.5/tests/test_context_project_summary.py +1545 -0
- {project_agent-0.2.2 → project_agent-0.2.5}/tests/test_context_resolve_binding.py +1 -1
- project_agent-0.2.5/tests/test_context_resolve_user.py +129 -0
- {project_agent-0.2.2 → project_agent-0.2.5}/tests/test_context_search_user.py +13 -4
- project_agent-0.2.5/tests/test_context_set_project_owner.py +289 -0
- {project_agent-0.2.2 → project_agent-0.2.5}/tests/test_context_upsert_user_identity.py +79 -5
- project_agent-0.2.5/tests/test_core_relationships.py +1040 -0
- {project_agent-0.2.2 → project_agent-0.2.5}/tests/test_db_migration.py +67 -0
- project_agent-0.2.5/tests/test_db_rename_channel.py +230 -0
- project_agent-0.2.5/tests/test_db_schema.py +739 -0
- project_agent-0.2.5/tests/test_db_session.py +168 -0
- {project_agent-0.2.2 → project_agent-0.2.5}/tests/test_doc_hub.py +648 -43
- project_agent-0.2.5/tests/test_doc_render_planned_sprints.py +992 -0
- {project_agent-0.2.2 → project_agent-0.2.5}/tests/test_loop_jobs.py +68 -0
- project_agent-0.2.5/tests/test_mind_approval_policy.py +91 -0
- {project_agent-0.2.2 → project_agent-0.2.5}/tests/test_mind_read.py +724 -329
- {project_agent-0.2.2 → project_agent-0.2.5}/tests/test_monitoring_plan.py +1 -1
- {project_agent-0.2.2 → project_agent-0.2.5}/tests/test_monitoring_run.py +205 -12
- project_agent-0.2.5/tests/test_mushroom_prompt_hooks.py +704 -0
- project_agent-0.2.5/tests/test_onboarding_apply.py +310 -0
- project_agent-0.2.5/tests/test_onboarding_apply_cli.py +120 -0
- {project_agent-0.2.2 → project_agent-0.2.5}/tests/test_overview_sync_hints.py +33 -33
- project_agent-0.2.5/tests/test_project_group_boundary_guard.py +1004 -0
- project_agent-0.2.5/tests/test_project_group_routing_contracts.py +209 -0
- project_agent-0.2.5/tests/test_proposal_apply.py +264 -0
- project_agent-0.2.5/tests/test_query_apply.py +986 -0
- project_agent-0.2.5/tests/test_query_cli.py +91 -0
- {project_agent-0.2.2 → project_agent-0.2.5}/tests/test_release_versioning.py +1 -1
- {project_agent-0.2.2 → project_agent-0.2.5}/tests/test_report_build.py +877 -128
- {project_agent-0.2.2 → project_agent-0.2.5}/tests/test_report_persistence.py +116 -45
- project_agent-0.2.5/tests/test_reporting_apply.py +106 -0
- {project_agent-0.2.2 → project_agent-0.2.5}/tests/test_runtime_bundles.py +143 -3
- {project_agent-0.2.2 → project_agent-0.2.5}/tests/test_runtime_output_hygiene_contracts.py +4 -18
- {project_agent-0.2.2 → project_agent-0.2.5}/tests/test_scheduled_run.py +828 -848
- project_agent-0.2.5/tests/test_sprint_apply.py +611 -0
- project_agent-0.2.5/tests/test_sprint_apply_cli.py +1205 -0
- project_agent-0.2.2/tests/test_version_closure_trigger.py → project_agent-0.2.5/tests/test_sprint_closure_trigger.py +229 -307
- project_agent-0.2.5/tests/test_sprint_rollover.py +349 -0
- project_agent-0.2.5/tests/test_sprint_rollover_cli.py +164 -0
- project_agent-0.2.2/tests/test_version_status_auto_sync.py → project_agent-0.2.5/tests/test_sprint_status_auto_sync.py +135 -98
- project_agent-0.2.5/tests/test_status_sensitive_task_reads.py +93 -0
- project_agent-0.2.5/tests/test_task_apply.py +1796 -0
- project_agent-0.2.5/tests/test_task_backend.py +3324 -0
- project_agent-0.2.5/tests/test_task_center_client.py +433 -0
- project_agent-0.2.5/tests/test_task_cli.py +497 -0
- project_agent-0.2.5/tests/test_task_migration.py +54 -0
- project_agent-0.2.5/tests/test_task_only_groups.py +845 -0
- project_agent-0.2.2/src/project_agent/application/context/get_project_context.py +0 -59
- project_agent-0.2.2/src/project_agent/application/context/get_project_owner.py +0 -53
- project_agent-0.2.2/src/project_agent/application/context/get_task.py +0 -15
- project_agent-0.2.2/src/project_agent/application/context/list_projects.py +0 -222
- project_agent-0.2.2/src/project_agent/application/context/list_tasks.py +0 -35
- project_agent-0.2.2/src/project_agent/application/mind/planned_versions.py +0 -32
- project_agent-0.2.2/src/project_agent/application/mind/read.py +0 -405
- project_agent-0.2.2/src/project_agent/application/mind/signoff_proposal.py +0 -303
- project_agent-0.2.2/src/project_agent/application/mind/update_version.py +0 -257
- project_agent-0.2.2/src/project_agent/application/mind/write_task_update.py +0 -314
- project_agent-0.2.2/src/project_agent/application/scheduled/run.py +0 -667
- project_agent-0.2.2/src/project_agent/application/version_closure/__init__.py +0 -3
- project_agent-0.2.2/src/project_agent/application/version_closure/evaluate.py +0 -86
- project_agent-0.2.2/src/project_agent/domain/mind/models.py +0 -255
- project_agent-0.2.2/src/project_agent/domain/shared/project_mind_derived.py +0 -143
- project_agent-0.2.2/src/project_agent/domain/version_closure/__init__.py +0 -13
- project_agent-0.2.2/src/project_agent/infrastructure/db/schema.py +0 -609
- project_agent-0.2.2/src/project_agent/interface/cli/commands/db.py +0 -28
- project_agent-0.2.2/src/project_agent/interface/cli/commands/mind.py +0 -1250
- project_agent-0.2.2/src/project_agent/interface/cli/commands/task_event_notify.py +0 -221
- project_agent-0.2.2/src/project_agent/interface/cli/commands/version_closure.py +0 -86
- project_agent-0.2.2/tests/test_cli_help.py +0 -194
- project_agent-0.2.2/tests/test_context_get_project_owner.py +0 -203
- project_agent-0.2.2/tests/test_context_get_project_pending_states.py +0 -179
- project_agent-0.2.2/tests/test_context_list_projects.py +0 -571
- project_agent-0.2.2/tests/test_context_list_tasks.py +0 -475
- project_agent-0.2.2/tests/test_context_project_summary.py +0 -322
- project_agent-0.2.2/tests/test_db_schema.py +0 -374
- project_agent-0.2.2/tests/test_db_session.py +0 -66
- project_agent-0.2.2/tests/test_doc_render_planned_versions.py +0 -498
- project_agent-0.2.2/tests/test_finalize_version_closure.py +0 -771
- project_agent-0.2.2/tests/test_mind_governance.py +0 -1112
- project_agent-0.2.2/tests/test_mind_proposal_extensions.py +0 -763
- project_agent-0.2.2/tests/test_mind_proposal_signoff.py +0 -452
- project_agent-0.2.2/tests/test_project_group_boundary_guard.py +0 -836
- project_agent-0.2.2/tests/test_project_group_routing_contracts.py +0 -150
- {project_agent-0.2.2 → project_agent-0.2.5}/README.md +0 -0
- {project_agent-0.2.2 → project_agent-0.2.5}/setup.cfg +0 -0
- {project_agent-0.2.2 → project_agent-0.2.5}/src/project_agent/__init__.py +0 -0
- {project_agent-0.2.2 → project_agent-0.2.5}/src/project_agent/application/__init__.py +0 -0
- {project_agent-0.2.2 → project_agent-0.2.5}/src/project_agent/application/bootstrap/__init__.py +0 -0
- {project_agent-0.2.2 → project_agent-0.2.5}/src/project_agent/application/context/__init__.py +0 -0
- {project_agent-0.2.2 → project_agent-0.2.5}/src/project_agent/application/context/binding_normalization.py +0 -0
- {project_agent-0.2.2 → project_agent-0.2.5}/src/project_agent/application/context/resolve_binding.py +0 -0
- {project_agent-0.2.2 → project_agent-0.2.5}/src/project_agent/application/context/resolve_group.py +0 -0
- {project_agent-0.2.2 → project_agent-0.2.5}/src/project_agent/application/doc/__init__.py +0 -0
- {project_agent-0.2.2 → project_agent-0.2.5}/src/project_agent/application/doc_hub/__init__.py +0 -0
- {project_agent-0.2.2 → project_agent-0.2.5}/src/project_agent/application/loop/__init__.py +0 -0
- {project_agent-0.2.2 → project_agent-0.2.5}/src/project_agent/application/mind/__init__.py +0 -0
- {project_agent-0.2.2 → project_agent-0.2.5}/src/project_agent/application/monitoring/__init__.py +0 -0
- {project_agent-0.2.2 → project_agent-0.2.5}/src/project_agent/application/monitoring/plan.py +0 -0
- {project_agent-0.2.2 → project_agent-0.2.5}/src/project_agent/application/report/__init__.py +0 -0
- {project_agent-0.2.2 → project_agent-0.2.5}/src/project_agent/application/scheduled/__init__.py +0 -0
- {project_agent-0.2.2 → project_agent-0.2.5}/src/project_agent/domain/__init__.py +0 -0
- {project_agent-0.2.2 → project_agent-0.2.5}/src/project_agent/domain/bootstrap/__init__.py +0 -0
- {project_agent-0.2.2 → project_agent-0.2.5}/src/project_agent/domain/doc/__init__.py +0 -0
- {project_agent-0.2.2 → project_agent-0.2.5}/src/project_agent/domain/doc_hub/__init__.py +0 -0
- {project_agent-0.2.2 → project_agent-0.2.5}/src/project_agent/domain/loop/__init__.py +0 -0
- {project_agent-0.2.2 → project_agent-0.2.5}/src/project_agent/domain/loop/models.py +0 -0
- {project_agent-0.2.2 → project_agent-0.2.5}/src/project_agent/domain/mind/__init__.py +0 -0
- {project_agent-0.2.2 → project_agent-0.2.5}/src/project_agent/domain/monitoring/__init__.py +0 -0
- {project_agent-0.2.2 → project_agent-0.2.5}/src/project_agent/domain/project_context/__init__.py +0 -0
- {project_agent-0.2.2 → project_agent-0.2.5}/src/project_agent/domain/report/__init__.py +0 -0
- {project_agent-0.2.2 → project_agent-0.2.5}/src/project_agent/domain/scheduled/__init__.py +0 -0
- {project_agent-0.2.2 → project_agent-0.2.5}/src/project_agent/domain/shared/__init__.py +0 -0
- {project_agent-0.2.2 → project_agent-0.2.5}/src/project_agent/domain/shared/models.py +0 -0
- {project_agent-0.2.2 → project_agent-0.2.5}/src/project_agent/domain/user/__init__.py +0 -0
- {project_agent-0.2.2 → project_agent-0.2.5}/src/project_agent/infrastructure/__init__.py +0 -0
- {project_agent-0.2.2 → project_agent-0.2.5}/src/project_agent/infrastructure/clock/__init__.py +0 -0
- {project_agent-0.2.2 → project_agent-0.2.5}/src/project_agent/infrastructure/db/__init__.py +0 -0
- {project_agent-0.2.2 → project_agent-0.2.5}/src/project_agent/infrastructure/db/migration.py +0 -0
- {project_agent-0.2.2 → project_agent-0.2.5}/src/project_agent/infrastructure/db/session.py +0 -0
- {project_agent-0.2.2 → project_agent-0.2.5}/src/project_agent/interface/__init__.py +0 -0
- {project_agent-0.2.2 → project_agent-0.2.5}/src/project_agent/interface/cli/__init__.py +0 -0
- {project_agent-0.2.2 → project_agent-0.2.5}/src/project_agent/interface/cli/commands/__init__.py +0 -0
- {project_agent-0.2.2 → project_agent-0.2.5}/src/project_agent/interface/cli/commands/loop.py +0 -0
- {project_agent-0.2.2 → project_agent-0.2.5}/src/project_agent/interface/cli/commands/project_boundary.py +0 -0
- {project_agent-0.2.2 → project_agent-0.2.5}/src/project_agent/interface/cli/commands/upgrade.py +0 -0
- {project_agent-0.2.2 → project_agent-0.2.5}/src/project_agent.egg-info/dependency_links.txt +0 -0
- {project_agent-0.2.2 → project_agent-0.2.5}/src/project_agent.egg-info/entry_points.txt +0 -0
- {project_agent-0.2.2 → project_agent-0.2.5}/src/project_agent.egg-info/requires.txt +0 -0
- {project_agent-0.2.2 → project_agent-0.2.5}/src/project_agent.egg-info/top_level.txt +0 -0
- {project_agent-0.2.2 → project_agent-0.2.5}/tests/test_pending_state_idempotency.py +0 -0
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "project-agent"
|
|
7
|
-
version = "0.2.
|
|
7
|
+
version = "0.2.5"
|
|
8
8
|
description = "Project Agent Python CLI core"
|
|
9
9
|
requires-python = ">=3.11"
|
|
10
10
|
dependencies = [
|
|
@@ -18,6 +18,8 @@ dependencies = [
|
|
|
18
18
|
|
|
19
19
|
[dependency-groups]
|
|
20
20
|
dev = [
|
|
21
|
+
"fastapi>=0.115,<1",
|
|
22
|
+
"httpx>=0.27,<1",
|
|
21
23
|
"pytest>=8.0",
|
|
22
24
|
]
|
|
23
25
|
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import Any, Protocol
|
|
4
|
+
|
|
5
|
+
from project_agent.application.bootstrap.contracts import (
|
|
6
|
+
BootstrapAcknowledgement,
|
|
7
|
+
BootstrapDraft,
|
|
8
|
+
BootstrapRequest,
|
|
9
|
+
)
|
|
10
|
+
from project_agent.application.shared.actor import ActorRef, ActorResolution
|
|
11
|
+
from project_agent.application.shared.scene_command import (
|
|
12
|
+
SceneError,
|
|
13
|
+
SceneOperationReceipt,
|
|
14
|
+
SceneReceipt,
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class BootstrapRuntime(Protocol):
|
|
19
|
+
def resolve_actor(self, actor_ref: ActorRef) -> ActorResolution: ...
|
|
20
|
+
|
|
21
|
+
def init(self, project_id: str, bootstrap: BootstrapDraft) -> dict[str, Any]: ...
|
|
22
|
+
|
|
23
|
+
def mark(self, project_id: str, milestone: str) -> None: ...
|
|
24
|
+
|
|
25
|
+
def record_result(
|
|
26
|
+
self,
|
|
27
|
+
project_id: str,
|
|
28
|
+
acknowledgement: BootstrapAcknowledgement,
|
|
29
|
+
) -> None: ...
|
|
30
|
+
|
|
31
|
+
def loop_state(
|
|
32
|
+
self,
|
|
33
|
+
project_id: str,
|
|
34
|
+
stuck_threshold: int,
|
|
35
|
+
) -> dict[str, Any]: ...
|
|
36
|
+
|
|
37
|
+
def commit(self) -> None: ...
|
|
38
|
+
|
|
39
|
+
def rollback(self) -> None: ...
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class BootstrapApply:
|
|
43
|
+
"""Combine init/acknowledgement, ledger update and the next checkpoint read."""
|
|
44
|
+
|
|
45
|
+
_LEGACY_DOC_ACTIONS = {"land_project_card", "backfill_docs"}
|
|
46
|
+
|
|
47
|
+
def __init__(self, *, runtime: BootstrapRuntime) -> None:
|
|
48
|
+
self._runtime = runtime
|
|
49
|
+
|
|
50
|
+
def apply(self, request: BootstrapRequest) -> SceneReceipt:
|
|
51
|
+
init_result = None
|
|
52
|
+
changed = False
|
|
53
|
+
try:
|
|
54
|
+
if request.action == "init":
|
|
55
|
+
bootstrap, identity_error = self._resolve_bootstrap_people(
|
|
56
|
+
request.bootstrap
|
|
57
|
+
)
|
|
58
|
+
if identity_error is not None:
|
|
59
|
+
return SceneReceipt(
|
|
60
|
+
status="failed",
|
|
61
|
+
results=[
|
|
62
|
+
SceneOperationReceipt(
|
|
63
|
+
operation_id=request.operation_id,
|
|
64
|
+
action=request.action,
|
|
65
|
+
status="failed",
|
|
66
|
+
error=identity_error,
|
|
67
|
+
)
|
|
68
|
+
],
|
|
69
|
+
)
|
|
70
|
+
init_result = self._runtime.init(request.project_id, bootstrap)
|
|
71
|
+
changed = True
|
|
72
|
+
if request.acknowledgement is not None:
|
|
73
|
+
acknowledgement = request.acknowledgement
|
|
74
|
+
if acknowledgement.milestone is not None:
|
|
75
|
+
self._runtime.mark(request.project_id, acknowledgement.milestone)
|
|
76
|
+
if acknowledgement.next_action is not None:
|
|
77
|
+
self._runtime.record_result(request.project_id, acknowledgement)
|
|
78
|
+
changed = True
|
|
79
|
+
|
|
80
|
+
loop_state = self._runtime.loop_state(
|
|
81
|
+
request.project_id,
|
|
82
|
+
request.stuck_threshold,
|
|
83
|
+
)
|
|
84
|
+
for _ in range(2):
|
|
85
|
+
next_action = self._next_action(loop_state)
|
|
86
|
+
if next_action not in self._LEGACY_DOC_ACTIONS:
|
|
87
|
+
break
|
|
88
|
+
self._runtime.record_result(
|
|
89
|
+
request.project_id,
|
|
90
|
+
BootstrapAcknowledgement(
|
|
91
|
+
next_action=next_action,
|
|
92
|
+
outcome="skipped",
|
|
93
|
+
reason="document_refresh_requires_user_request",
|
|
94
|
+
),
|
|
95
|
+
)
|
|
96
|
+
changed = True
|
|
97
|
+
loop_state = self._runtime.loop_state(
|
|
98
|
+
request.project_id,
|
|
99
|
+
request.stuck_threshold,
|
|
100
|
+
)
|
|
101
|
+
self._runtime.commit()
|
|
102
|
+
except Exception as exc:
|
|
103
|
+
self._runtime.rollback()
|
|
104
|
+
return SceneReceipt(
|
|
105
|
+
status="failed",
|
|
106
|
+
results=[
|
|
107
|
+
SceneOperationReceipt(
|
|
108
|
+
operation_id=request.operation_id,
|
|
109
|
+
action=request.action,
|
|
110
|
+
status="failed",
|
|
111
|
+
error=SceneError(
|
|
112
|
+
code="bootstrap_apply_failed",
|
|
113
|
+
message=str(exc),
|
|
114
|
+
retryable=True,
|
|
115
|
+
next_action="retry",
|
|
116
|
+
),
|
|
117
|
+
)
|
|
118
|
+
],
|
|
119
|
+
)
|
|
120
|
+
|
|
121
|
+
if loop_state.get("status") == "not_found":
|
|
122
|
+
return SceneReceipt(
|
|
123
|
+
status="failed",
|
|
124
|
+
results=[
|
|
125
|
+
SceneOperationReceipt(
|
|
126
|
+
operation_id=request.operation_id,
|
|
127
|
+
action=request.action,
|
|
128
|
+
status="failed",
|
|
129
|
+
error=SceneError(
|
|
130
|
+
code="project_not_found",
|
|
131
|
+
message=f"Project not found: {request.project_id}",
|
|
132
|
+
),
|
|
133
|
+
)
|
|
134
|
+
],
|
|
135
|
+
)
|
|
136
|
+
return SceneReceipt(
|
|
137
|
+
status="ok",
|
|
138
|
+
results=[
|
|
139
|
+
SceneOperationReceipt(
|
|
140
|
+
operation_id=request.operation_id,
|
|
141
|
+
action=request.action,
|
|
142
|
+
status="applied" if changed else "unchanged",
|
|
143
|
+
data={
|
|
144
|
+
**({"bootstrap": init_result} if init_result is not None else {}),
|
|
145
|
+
"loopState": loop_state,
|
|
146
|
+
"nextEffect": self._next_effect(loop_state),
|
|
147
|
+
},
|
|
148
|
+
)
|
|
149
|
+
],
|
|
150
|
+
)
|
|
151
|
+
|
|
152
|
+
def _resolve_bootstrap_people(
|
|
153
|
+
self,
|
|
154
|
+
bootstrap: BootstrapDraft,
|
|
155
|
+
) -> tuple[BootstrapDraft, SceneError | None]:
|
|
156
|
+
owner_user_id = bootstrap.owner_user_id
|
|
157
|
+
if bootstrap.owner_ref is not None:
|
|
158
|
+
resolution = self._runtime.resolve_actor(bootstrap.owner_ref)
|
|
159
|
+
if resolution.status != "resolved" or not resolution.user_id:
|
|
160
|
+
return bootstrap, SceneError(
|
|
161
|
+
code=f"bootstrap_owner_identity_{resolution.status}",
|
|
162
|
+
message=f"Bootstrap owner identity is {resolution.status}",
|
|
163
|
+
)
|
|
164
|
+
owner_user_id = resolution.user_id
|
|
165
|
+
|
|
166
|
+
participant_user_ids = list(bootstrap.participant_user_ids)
|
|
167
|
+
for participant_ref in bootstrap.participant_refs:
|
|
168
|
+
resolution = self._runtime.resolve_actor(participant_ref)
|
|
169
|
+
if resolution.status != "resolved" or not resolution.user_id:
|
|
170
|
+
return bootstrap, SceneError(
|
|
171
|
+
code=f"bootstrap_participant_identity_{resolution.status}",
|
|
172
|
+
message=f"Bootstrap participant identity is {resolution.status}",
|
|
173
|
+
)
|
|
174
|
+
participant_user_ids.append(resolution.user_id)
|
|
175
|
+
|
|
176
|
+
return (
|
|
177
|
+
bootstrap.model_copy(
|
|
178
|
+
update={
|
|
179
|
+
"owner_user_id": owner_user_id,
|
|
180
|
+
"owner_ref": None,
|
|
181
|
+
"participant_user_ids": participant_user_ids,
|
|
182
|
+
"participant_refs": [],
|
|
183
|
+
}
|
|
184
|
+
),
|
|
185
|
+
None,
|
|
186
|
+
)
|
|
187
|
+
|
|
188
|
+
@staticmethod
|
|
189
|
+
def _next_action(loop_state: dict[str, Any]) -> str | None:
|
|
190
|
+
return (loop_state.get("checkpoint") or {}).get("nextAction")
|
|
191
|
+
|
|
192
|
+
@classmethod
|
|
193
|
+
def _next_effect(cls, loop_state: dict[str, Any]) -> dict[str, Any]:
|
|
194
|
+
checkpoint = loop_state.get("checkpoint") or {}
|
|
195
|
+
next_action = checkpoint.get("nextAction")
|
|
196
|
+
if loop_state.get("status") == "blocked":
|
|
197
|
+
return {
|
|
198
|
+
"action": "report_blocker",
|
|
199
|
+
"blocker": loop_state.get("blocker"),
|
|
200
|
+
}
|
|
201
|
+
if next_action == "create_group_binding":
|
|
202
|
+
return {"action": "create_project_group_binding"}
|
|
203
|
+
if next_action == "send_alignment_intro":
|
|
204
|
+
return {
|
|
205
|
+
"action": "send_template",
|
|
206
|
+
"template": "group-alignment-intro.md",
|
|
207
|
+
}
|
|
208
|
+
if next_action == "collect_alignment_fields":
|
|
209
|
+
return {
|
|
210
|
+
"action": "request_alignment_fields",
|
|
211
|
+
"fields": checkpoint.get("alignmentGaps") or [],
|
|
212
|
+
}
|
|
213
|
+
if next_action == "send_execution_confirmation":
|
|
214
|
+
return {
|
|
215
|
+
"action": "send_template",
|
|
216
|
+
"template": "execution-start-confirmation.md",
|
|
217
|
+
}
|
|
218
|
+
if next_action == "await_owner_confirmation":
|
|
219
|
+
return {"action": "await_owner_confirmation"}
|
|
220
|
+
if next_action == "bootstrap_complete":
|
|
221
|
+
return {"action": "complete"}
|
|
222
|
+
return {"action": "stop"}
|
{project_agent-0.2.2 → project_agent-0.2.5}/src/project_agent/application/bootstrap/checkpoint.py
RENAMED
|
@@ -9,8 +9,8 @@ from project_agent.domain.bootstrap.models import (
|
|
|
9
9
|
)
|
|
10
10
|
from project_agent.domain.shared.project_mind_derived import (
|
|
11
11
|
describe_missing_task_fields,
|
|
12
|
-
|
|
13
|
-
|
|
12
|
+
describe_missing_sprint_fields,
|
|
13
|
+
resolve_current_sprint,
|
|
14
14
|
)
|
|
15
15
|
|
|
16
16
|
MILESTONE_ALIGNMENT_INTRO = "alignment_intro_sent"
|
|
@@ -31,7 +31,7 @@ class BootstrapReadStore(Protocol):
|
|
|
31
31
|
|
|
32
32
|
def list_bindings_by_project_id(self, project_id: str) -> list[Any]: ...
|
|
33
33
|
|
|
34
|
-
def
|
|
34
|
+
def list_sprints_by_project_id(self, project_id: str) -> list[Any]: ...
|
|
35
35
|
|
|
36
36
|
def list_tasks_by_project_id(self, project_id: str) -> list[Any]: ...
|
|
37
37
|
|
|
@@ -115,10 +115,10 @@ def evaluate_bootstrap_checkpoint(
|
|
|
115
115
|
has_project_card_doc = "project_card" in landed_doc_kinds
|
|
116
116
|
doc_gaps = [kind for kind in REQUIRED_BOOTSTRAP_DOC_KINDS if kind not in landed_doc_kinds]
|
|
117
117
|
|
|
118
|
-
|
|
118
|
+
sprints = bootstrap_store.list_sprints_by_project_id(project_id)
|
|
119
119
|
tasks = bootstrap_store.list_tasks_by_project_id(project_id)
|
|
120
|
-
alignment_gaps = _build_alignment_gaps(project=project,
|
|
121
|
-
alignment_ready = _is_alignment_ready(project=project,
|
|
120
|
+
alignment_gaps = _build_alignment_gaps(project=project, sprints=sprints, tasks=tasks)
|
|
121
|
+
alignment_ready = _is_alignment_ready(project=project, sprints=sprints, tasks=tasks)
|
|
122
122
|
|
|
123
123
|
mind_stage = project.get("mind_stage") or "v0"
|
|
124
124
|
kickoff_sent = _is_milestone_marked(
|
|
@@ -126,7 +126,7 @@ def evaluate_bootstrap_checkpoint(
|
|
|
126
126
|
state_key=kickoff_state_key(project_id),
|
|
127
127
|
pending_state_store=pending_state_store,
|
|
128
128
|
)
|
|
129
|
-
# bootstrap 是否仍在进行,以 kickoff 是否已落账为准:
|
|
129
|
+
# bootstrap 是否仍在进行,以 kickoff 是否已落账为准:Sprint scene 创建会在团队对齐期
|
|
130
130
|
# 把 mind_stage 提前翻成 v1,但只要 kickoff 还没发,bootstrap 就还没结束。
|
|
131
131
|
bootstrap_active = not kickoff_sent
|
|
132
132
|
|
|
@@ -164,8 +164,6 @@ def evaluate_bootstrap_checkpoint(
|
|
|
164
164
|
next_action=_select_next_action(
|
|
165
165
|
bootstrap_active=bootstrap_active,
|
|
166
166
|
has_group_binding=has_group_binding,
|
|
167
|
-
has_project_card_doc=has_project_card_doc,
|
|
168
|
-
doc_gaps=doc_gaps,
|
|
169
167
|
intro_status=intro_status,
|
|
170
168
|
alignment_ready=alignment_ready,
|
|
171
169
|
confirmation_status=confirmation_status,
|
|
@@ -231,17 +229,17 @@ def mark_bootstrap_milestone(
|
|
|
231
229
|
|
|
232
230
|
|
|
233
231
|
def _build_alignment_gaps(
|
|
234
|
-
*, project: dict,
|
|
232
|
+
*, project: dict, sprints: list[dict], tasks: list[dict]
|
|
235
233
|
) -> list[str]:
|
|
236
|
-
|
|
237
|
-
if
|
|
238
|
-
return ["
|
|
234
|
+
current_sprint = resolve_current_sprint(project=project, sprints=sprints)
|
|
235
|
+
if current_sprint is None:
|
|
236
|
+
return ["missing_current_sprint"]
|
|
239
237
|
current_tasks = [
|
|
240
|
-
task for task in tasks if task.get("
|
|
238
|
+
task for task in tasks if task.get("sprint_id") == current_sprint.get("sprint_id")
|
|
241
239
|
]
|
|
242
240
|
gaps = [
|
|
243
|
-
f"
|
|
244
|
-
for field in
|
|
241
|
+
f"current_sprint:{field}"
|
|
242
|
+
for field in describe_missing_sprint_fields(sprint=current_sprint, tasks=current_tasks)
|
|
245
243
|
]
|
|
246
244
|
if not current_tasks:
|
|
247
245
|
gaps.append("missing_trackable_task")
|
|
@@ -252,14 +250,14 @@ def _build_alignment_gaps(
|
|
|
252
250
|
return gaps
|
|
253
251
|
|
|
254
252
|
|
|
255
|
-
def _is_alignment_ready(*, project: dict,
|
|
256
|
-
|
|
257
|
-
if
|
|
253
|
+
def _is_alignment_ready(*, project: dict, sprints: list[dict], tasks: list[dict]) -> bool:
|
|
254
|
+
current_sprint = resolve_current_sprint(project=project, sprints=sprints)
|
|
255
|
+
if current_sprint is None:
|
|
258
256
|
return False
|
|
259
257
|
current_tasks = [
|
|
260
|
-
task for task in tasks if task.get("
|
|
258
|
+
task for task in tasks if task.get("sprint_id") == current_sprint.get("sprint_id")
|
|
261
259
|
]
|
|
262
|
-
if
|
|
260
|
+
if describe_missing_sprint_fields(sprint=current_sprint, tasks=current_tasks):
|
|
263
261
|
return False
|
|
264
262
|
# 执行启动确认只要求至少一个可追踪 Task 必填字段齐全。
|
|
265
263
|
return any(not describe_missing_task_fields(task=task) for task in current_tasks)
|
|
@@ -345,8 +343,6 @@ def _select_next_action(
|
|
|
345
343
|
*,
|
|
346
344
|
bootstrap_active: bool,
|
|
347
345
|
has_group_binding: bool,
|
|
348
|
-
has_project_card_doc: bool,
|
|
349
|
-
doc_gaps: list[str],
|
|
350
346
|
intro_status: str,
|
|
351
347
|
alignment_ready: bool,
|
|
352
348
|
confirmation_status: str,
|
|
@@ -355,20 +351,15 @@ def _select_next_action(
|
|
|
355
351
|
return "bootstrap_complete"
|
|
356
352
|
if not has_group_binding:
|
|
357
353
|
return "create_group_binding"
|
|
358
|
-
# 对齐引导唯一硬前置是 Project Card;缺它先驱动文档落地。
|
|
359
|
-
if intro_status == "pending" and not has_project_card_doc:
|
|
360
|
-
return "land_project_card"
|
|
361
354
|
if intro_status == "pending":
|
|
362
355
|
return "send_alignment_intro"
|
|
363
356
|
if confirmation_status == "pending":
|
|
364
357
|
return "send_execution_confirmation"
|
|
365
|
-
# 消息欠账还清后,剩余文档欠账由 agent 当轮补齐,不阻塞人侧流程。
|
|
366
|
-
if doc_gaps:
|
|
367
|
-
return "backfill_docs"
|
|
368
358
|
if not alignment_ready:
|
|
369
359
|
return "collect_alignment_fields"
|
|
370
360
|
if confirmation_status == "sent":
|
|
371
361
|
return "await_owner_confirmation"
|
|
362
|
+
# 文档缺口只保留为待更新提示,不进入 bootstrap action 选择。
|
|
372
363
|
return "collect_alignment_fields"
|
|
373
364
|
|
|
374
365
|
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
from typing import Any, Literal
|
|
2
|
+
|
|
3
|
+
from pydantic import Field, model_validator
|
|
4
|
+
|
|
5
|
+
from project_agent.application.shared.actor import ActorRef
|
|
6
|
+
from project_agent.domain.shared.models import ProjectAgentModel
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class BootstrapDraft(ProjectAgentModel):
|
|
10
|
+
project_name: str
|
|
11
|
+
owner_user_id: str | None = None
|
|
12
|
+
owner_ref: ActorRef | None = None
|
|
13
|
+
primary_goal: str
|
|
14
|
+
target_release_date: str | None = None
|
|
15
|
+
timezone: str = "Asia/Shanghai"
|
|
16
|
+
channel: str | None = None
|
|
17
|
+
project_group_id: str | None = None
|
|
18
|
+
participant_user_ids: list[str] = Field(default_factory=list)
|
|
19
|
+
participant_refs: list[ActorRef] = Field(default_factory=list)
|
|
20
|
+
|
|
21
|
+
@model_validator(mode="after")
|
|
22
|
+
def validate_people_references(self) -> "BootstrapDraft":
|
|
23
|
+
if (self.owner_user_id is None) == (self.owner_ref is None):
|
|
24
|
+
raise ValueError(
|
|
25
|
+
"bootstrap requires exactly one ownerUserId or ownerRef"
|
|
26
|
+
)
|
|
27
|
+
if self.participant_user_ids and self.participant_refs:
|
|
28
|
+
raise ValueError(
|
|
29
|
+
"bootstrap accepts participantUserIds or participantRefs, not both"
|
|
30
|
+
)
|
|
31
|
+
return self
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class BootstrapAcknowledgement(ProjectAgentModel):
|
|
35
|
+
next_action: Literal[
|
|
36
|
+
"create_group_binding",
|
|
37
|
+
"land_project_card",
|
|
38
|
+
"send_alignment_intro",
|
|
39
|
+
"backfill_docs",
|
|
40
|
+
"collect_alignment_fields",
|
|
41
|
+
"send_execution_confirmation",
|
|
42
|
+
] | None = None
|
|
43
|
+
outcome: Literal["success", "failed", "skipped"] | None = None
|
|
44
|
+
milestone: Literal[
|
|
45
|
+
"alignment_intro_sent",
|
|
46
|
+
"execution_confirmation_sent",
|
|
47
|
+
"kickoff_sent",
|
|
48
|
+
] | None = None
|
|
49
|
+
stage: str | None = None
|
|
50
|
+
reason: str | None = None
|
|
51
|
+
details: dict[str, Any] = Field(default_factory=dict)
|
|
52
|
+
|
|
53
|
+
@model_validator(mode="after")
|
|
54
|
+
def validate_result_pair(self) -> "BootstrapAcknowledgement":
|
|
55
|
+
if (self.next_action is None) != (self.outcome is None):
|
|
56
|
+
raise ValueError("nextAction and outcome must be provided together")
|
|
57
|
+
if self.next_action is None and self.milestone is None:
|
|
58
|
+
raise ValueError("acknowledgement requires a loop result or milestone")
|
|
59
|
+
return self
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
class BootstrapRequest(ProjectAgentModel):
|
|
63
|
+
operation_id: str
|
|
64
|
+
action: Literal["init", "advance"]
|
|
65
|
+
project_id: str
|
|
66
|
+
bootstrap: BootstrapDraft | None = None
|
|
67
|
+
acknowledgement: BootstrapAcknowledgement | None = None
|
|
68
|
+
stuck_threshold: int = 3
|
|
69
|
+
|
|
70
|
+
@model_validator(mode="after")
|
|
71
|
+
def validate_action_fields(self) -> "BootstrapRequest":
|
|
72
|
+
if not self.operation_id:
|
|
73
|
+
raise ValueError("operationId must be non-empty")
|
|
74
|
+
if self.action == "init" and self.bootstrap is None:
|
|
75
|
+
raise ValueError("init requires bootstrap")
|
|
76
|
+
if self.action == "advance" and self.bootstrap is not None:
|
|
77
|
+
raise ValueError("advance does not accept bootstrap")
|
|
78
|
+
if self.stuck_threshold < 1:
|
|
79
|
+
raise ValueError("stuckThreshold must be at least 1")
|
|
80
|
+
return self
|
|
@@ -13,7 +13,7 @@ from project_agent.domain.bootstrap.models import BootstrapBinding, BootstrapIni
|
|
|
13
13
|
from project_agent.domain.shared.project_mind_derived import (
|
|
14
14
|
build_project_intro,
|
|
15
15
|
collect_participants,
|
|
16
|
-
|
|
16
|
+
resolve_current_sprint,
|
|
17
17
|
resolve_target_release_date,
|
|
18
18
|
)
|
|
19
19
|
from project_agent.domain.shared.project_mind_readiness import (
|
|
@@ -31,7 +31,7 @@ class BootstrapStore(Protocol):
|
|
|
31
31
|
|
|
32
32
|
def list_bindings_by_project_id(self, project_id: str) -> list[Any]: ...
|
|
33
33
|
|
|
34
|
-
def
|
|
34
|
+
def list_sprints_by_project_id(self, project_id: str) -> list[Any]: ...
|
|
35
35
|
|
|
36
36
|
def list_tasks_by_project_id(self, project_id: str) -> list[Any]: ...
|
|
37
37
|
|
|
@@ -70,7 +70,7 @@ def init_project_bootstrap(
|
|
|
70
70
|
"mind_stage": (existing_project or {}).get("mind_stage") or "v0",
|
|
71
71
|
"lifecycle_status": (existing_project or {}).get("lifecycle_status") or "active",
|
|
72
72
|
"current_revision_id": (existing_project or {}).get("current_revision_id"),
|
|
73
|
-
"
|
|
73
|
+
"current_sprint_id": (existing_project or {}).get("current_sprint_id"),
|
|
74
74
|
"target_release_date": target_release_date
|
|
75
75
|
if target_release_date is not None
|
|
76
76
|
else (existing_project or {}).get("target_release_date"),
|
|
@@ -134,12 +134,12 @@ def init_project_bootstrap(
|
|
|
134
134
|
)
|
|
135
135
|
for binding in bootstrap_store.list_bindings_by_project_id(project_id)
|
|
136
136
|
]
|
|
137
|
-
|
|
137
|
+
sprints = bootstrap_store.list_sprints_by_project_id(project_id)
|
|
138
138
|
tasks = bootstrap_store.list_tasks_by_project_id(project_id)
|
|
139
139
|
stored_participants = bootstrap_store.list_participants_by_project_id(project_id)
|
|
140
140
|
readiness_participants = [
|
|
141
141
|
*stored_participants,
|
|
142
|
-
*_participant_rows_from_project_mind(
|
|
142
|
+
*_participant_rows_from_project_mind(sprints=sprints, tasks=tasks),
|
|
143
143
|
]
|
|
144
144
|
documents = bootstrap_store.list_documents_by_project_id(project_id)
|
|
145
145
|
monitoring_policy = bootstrap_store.find_monitoring_policy_by_project_id(project_id)
|
|
@@ -152,11 +152,11 @@ def init_project_bootstrap(
|
|
|
152
152
|
bindings=[binding.model_dump(mode="json", by_alias=False) for binding in bindings],
|
|
153
153
|
monitoring_enabled=bool((monitoring_policy or {}).get("enabled")),
|
|
154
154
|
documents=documents,
|
|
155
|
-
|
|
155
|
+
sprints=sprints,
|
|
156
156
|
tasks=tasks,
|
|
157
157
|
participants=readiness_participants,
|
|
158
158
|
)
|
|
159
|
-
|
|
159
|
+
current_sprint = resolve_current_sprint(project=project, sprints=sprints)
|
|
160
160
|
|
|
161
161
|
return BootstrapInitResult(
|
|
162
162
|
project_id=project["project_id"],
|
|
@@ -165,14 +165,14 @@ def init_project_bootstrap(
|
|
|
165
165
|
primary_goal=project.get("primary_goal") or "",
|
|
166
166
|
target_release_date=resolve_target_release_date(
|
|
167
167
|
project=project,
|
|
168
|
-
|
|
168
|
+
current_sprint=current_sprint,
|
|
169
169
|
),
|
|
170
170
|
participants=collect_participants(
|
|
171
|
-
|
|
171
|
+
sprints=sprints,
|
|
172
172
|
tasks=tasks,
|
|
173
173
|
participants=stored_participants,
|
|
174
174
|
),
|
|
175
|
-
project_intro=build_project_intro(project=project,
|
|
175
|
+
project_intro=build_project_intro(project=project, sprints=sprints, tasks=tasks),
|
|
176
176
|
last_updated_at=project.get("last_updated_at"),
|
|
177
177
|
mind_stage=project["mind_stage"],
|
|
178
178
|
lifecycle_status=project["lifecycle_status"],
|
|
@@ -194,8 +194,8 @@ def _dedupe_participant_user_ids(values: list[str]) -> list[str]:
|
|
|
194
194
|
return ordered
|
|
195
195
|
|
|
196
196
|
|
|
197
|
-
def _participant_rows_from_project_mind(*,
|
|
197
|
+
def _participant_rows_from_project_mind(*, sprints: list[dict], tasks: list[dict]) -> list[dict]:
|
|
198
198
|
return [
|
|
199
199
|
{"user_id": user_id, "status": "active"}
|
|
200
|
-
for user_id in collect_participants(
|
|
200
|
+
for user_id in collect_participants(sprints=sprints, tasks=tasks)
|
|
201
201
|
]
|
{project_agent-0.2.2 → project_agent-0.2.5}/src/project_agent/application/bootstrap/loop_state.py
RENAMED
|
@@ -17,7 +17,7 @@ class BootstrapReadStore(Protocol):
|
|
|
17
17
|
|
|
18
18
|
def list_bindings_by_project_id(self, project_id: str) -> list[Any]: ...
|
|
19
19
|
|
|
20
|
-
def
|
|
20
|
+
def list_sprints_by_project_id(self, project_id: str) -> list[Any]: ...
|
|
21
21
|
|
|
22
22
|
def list_tasks_by_project_id(self, project_id: str) -> list[Any]: ...
|
|
23
23
|
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
from typing import Any, Protocol
|
|
2
|
+
|
|
3
|
+
from project_agent.domain.project_context.models import AddProjectMemberResult, ProjectParticipantSummary
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class ProjectStore(Protocol):
|
|
7
|
+
def find_by_id(self, project_id: str) -> Any: ...
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class UserStore(Protocol):
|
|
11
|
+
def find_by_id(self, user_id: str) -> Any: ...
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class ParticipantWriteStore(Protocol):
|
|
15
|
+
def upsert_participant(self, payload: dict) -> dict: ...
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def add_project_member(
|
|
19
|
+
*,
|
|
20
|
+
project_id: str,
|
|
21
|
+
user_id: str,
|
|
22
|
+
project_store: ProjectStore,
|
|
23
|
+
user_store: UserStore,
|
|
24
|
+
participant_store: ParticipantWriteStore,
|
|
25
|
+
) -> AddProjectMemberResult:
|
|
26
|
+
if not project_store.find_by_id(project_id):
|
|
27
|
+
raise ValueError(f"project not found: {project_id}")
|
|
28
|
+
if not user_store.find_by_id(user_id):
|
|
29
|
+
raise ValueError(f"user not found: {user_id}")
|
|
30
|
+
|
|
31
|
+
participant = participant_store.upsert_participant(
|
|
32
|
+
{
|
|
33
|
+
"project_id": project_id,
|
|
34
|
+
"user_id": user_id,
|
|
35
|
+
"role": "member",
|
|
36
|
+
"status": "active",
|
|
37
|
+
}
|
|
38
|
+
)
|
|
39
|
+
return AddProjectMemberResult(
|
|
40
|
+
status="ok",
|
|
41
|
+
participant=ProjectParticipantSummary(
|
|
42
|
+
participant_id=participant["participant_id"],
|
|
43
|
+
project_id=participant["project_id"],
|
|
44
|
+
user_id=participant["user_id"],
|
|
45
|
+
role=participant["role"],
|
|
46
|
+
status=participant["status"],
|
|
47
|
+
created_at=participant.get("created_at"),
|
|
48
|
+
updated_at=participant.get("updated_at"),
|
|
49
|
+
),
|
|
50
|
+
)
|