@misterhuydo/sentinel 1.5.16 → 1.5.18
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.
- package/.cairn/.hint-lock +1 -1
- package/.cairn/session.json +11 -3
- package/package.json +1 -1
- package/python/sentinel/__init__.py +1 -1
- package/python/sentinel/config_loader.py +68 -14
- package/python/sentinel/dependency_manager.py +2 -2
- package/python/sentinel/git_manager.py +11 -11
- package/python/sentinel/main.py +32 -16
- package/python/sentinel/repo_task_engine.py +14 -4
- package/python/sentinel/sentinel_boss.py +26 -17
- package/python/sentinel/state_store.py +37 -0
package/.cairn/.hint-lock
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
2026-04-
|
|
1
|
+
2026-04-08T15:32:44.554Z
|
package/.cairn/session.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
|
-
"message": "Auto-checkpoint at 2026-04-
|
|
3
|
-
"checkpoint_at": "2026-04-
|
|
2
|
+
"message": "Auto-checkpoint at 2026-04-08T15:54:13.776Z",
|
|
3
|
+
"checkpoint_at": "2026-04-08T15:54:13.872Z",
|
|
4
4
|
"active_files": [
|
|
5
5
|
"J:\\Projects\\Sentinel\\cli\\bin\\sentinel.js",
|
|
6
6
|
"J:\\Projects\\Sentinel\\cli\\lib\\test.js",
|
|
@@ -20,7 +20,15 @@
|
|
|
20
20
|
"[2026-04-08] git-snapshot: .cairn/session.json | 29 ++++-\n .claude/settings.local.json | 47 ++++++-\n cli/.cairn/.hint-lock | 2 +-\n cli/.cairn/minify-map.json | 8 +-\n cli/.cairn/session.json | 29 ++++-\n cli/.cairn/views/62a614_bundle.js | 6 +-\n cli/lib/.cairn/minify-map.json | 6 +\n cli/lib/.cairn/views/fb78ac_upgrade.js | 37 +++++-\n cli/lib/.cairn/views/fc4a1a_add.js | 215 +++++++++++++++++++++++++--------\n 9 files changed, 313 insertions(+), 66 deletions(-) | status: M ../.cairn/session.json\n M ../.claude/settings.local.json\n M .cairn/.hint-lock\n M .cairn/minify-map.json\n M .cairn/session.json\n M .cairn/views/62a614_bundle.js\n M lib/.cairn/minify-map.json\n M lib/.cairn/views/fb78ac_upgrade.js\n M lib/.cairn/views/fc4a1a_add.js\n?? ../.cairn/.cairn-project\n?? ../.cairn/memory/\n?? ../.cairn/minify-map.json\n?? ../.cairn/views/\n?? .cairn/views/23edf4_sentinel_boss.py\n?? .cairn/views/7802b9_cicd_trigger.py\n?? .cairn/views/ac3df4_repo_task_engine.py\n?? lib/.cairn/views/2a85cc_init.js\n?? lib/.cairn/views/e26996_slack-setup.js\n?? ../scripts/fix_ask_codebase_context.py\n?? ../scripts/fix_ask_codebase_stdin.py\n?? ../scripts/fix_chain_slack.py\n?? ../scripts/fix_fstring.py\n?? ../scripts/fix_knowledge_cache.py\n?? ../scripts/fix_knowledge_cache_staleness.py\n?? ../scripts/fix_merge_confirm.py\n?? ../scripts/fix_permission_messages.py\n?? ../scripts/fix_pr_check_head_detect.py\n?? ../scripts/fix_pr_msg_newlines.py\n?? ../scripts/fix_pr_tracking_boss.py\n?? ../scripts/fix_pr_tracking_db.py\n?? ../scripts/fix_pr_tracking_main.py\n?? ../scripts/fix_project_isolation.py\n?? ../scripts/fix_system_prompt.py\n?? ../scripts/fix_two_bugs.py\n?? ../scripts/patch_chain_release.py",
|
|
21
21
|
"[2026-04-08] git-snapshot: .cairn/session.json | 29 ++++-\n .claude/settings.local.json | 47 ++++++-\n cli/.cairn/.hint-lock | 2 +-\n cli/.cairn/minify-map.json | 8 +-\n cli/.cairn/session.json | 30 ++++-\n cli/.cairn/views/62a614_bundle.js | 6 +-\n cli/lib/.cairn/minify-map.json | 6 +\n cli/lib/.cairn/views/fb78ac_upgrade.js | 37 +++++-\n cli/lib/.cairn/views/fc4a1a_add.js | 215 +++++++++++++++++++++++++--------\n 9 files changed, 314 insertions(+), 66 deletions(-) | status: M ../.cairn/session.json\n M ../.claude/settings.local.json\n M .cairn/.hint-lock\n M .cairn/minify-map.json\n M .cairn/session.json\n M .cairn/views/62a614_bundle.js\n M lib/.cairn/minify-map.json\n M lib/.cairn/views/fb78ac_upgrade.js\n M lib/.cairn/views/fc4a1a_add.js\n?? ../.cairn/.cairn-project\n?? ../.cairn/memory/\n?? ../.cairn/minify-map.json\n?? ../.cairn/views/\n?? .cairn/views/23edf4_sentinel_boss.py\n?? .cairn/views/7802b9_cicd_trigger.py\n?? .cairn/views/ac3df4_repo_task_engine.py\n?? lib/.cairn/views/2a85cc_init.js\n?? lib/.cairn/views/e26996_slack-setup.js\n?? ../scripts/fix_ask_codebase_context.py\n?? ../scripts/fix_ask_codebase_stdin.py\n?? ../scripts/fix_chain_slack.py\n?? ../scripts/fix_fstring.py\n?? ../scripts/fix_knowledge_cache.py\n?? ../scripts/fix_knowledge_cache_staleness.py\n?? ../scripts/fix_merge_confirm.py\n?? ../scripts/fix_permission_messages.py\n?? ../scripts/fix_pr_check_head_detect.py\n?? ../scripts/fix_pr_msg_newlines.py\n?? ../scripts/fix_pr_tracking_boss.py\n?? ../scripts/fix_pr_tracking_db.py\n?? ../scripts/fix_pr_tracking_main.py\n?? ../scripts/fix_project_isolation.py\n?? ../scripts/fix_system_prompt.py\n?? ../scripts/fix_two_bugs.py\n?? ../scripts/patch_chain_release.py",
|
|
22
22
|
"[2026-04-08] git-snapshot: .cairn/session.json | 29 ++++-\n .claude/settings.local.json | 47 ++++++-\n cli/.cairn/.hint-lock | 2 +-\n cli/.cairn/minify-map.json | 8 +-\n cli/.cairn/session.json | 31 ++++-\n cli/.cairn/views/62a614_bundle.js | 6 +-\n cli/lib/.cairn/minify-map.json | 6 +\n cli/lib/.cairn/views/fb78ac_upgrade.js | 37 +++++-\n cli/lib/.cairn/views/fc4a1a_add.js | 215 +++++++++++++++++++++++++--------\n 9 files changed, 315 insertions(+), 66 deletions(-) | status: M ../.cairn/session.json\n M ../.claude/settings.local.json\n M .cairn/.hint-lock\n M .cairn/minify-map.json\n M .cairn/session.json\n M .cairn/views/62a614_bundle.js\n M lib/.cairn/minify-map.json\n M lib/.cairn/views/fb78ac_upgrade.js\n M lib/.cairn/views/fc4a1a_add.js\n?? ../.cairn/.cairn-project\n?? ../.cairn/memory/\n?? ../.cairn/minify-map.json\n?? ../.cairn/views/\n?? .cairn/views/23edf4_sentinel_boss.py\n?? .cairn/views/7802b9_cicd_trigger.py\n?? .cairn/views/ac3df4_repo_task_engine.py\n?? lib/.cairn/views/2a85cc_init.js\n?? lib/.cairn/views/e26996_slack-setup.js\n?? ../scripts/fix_ask_codebase_context.py\n?? ../scripts/fix_ask_codebase_stdin.py\n?? ../scripts/fix_chain_slack.py\n?? ../scripts/fix_fstring.py\n?? ../scripts/fix_knowledge_cache.py\n?? ../scripts/fix_knowledge_cache_staleness.py\n?? ../scripts/fix_merge_confirm.py\n?? ../scripts/fix_permission_messages.py\n?? ../scripts/fix_pr_check_head_detect.py\n?? ../scripts/fix_pr_msg_newlines.py\n?? ../scripts/fix_pr_tracking_boss.py\n?? ../scripts/fix_pr_tracking_db.py\n?? ../scripts/fix_pr_tracking_main.py\n?? ../scripts/fix_project_isolation.py\n?? ../scripts/fix_system_prompt.py\n?? ../scripts/fix_two_bugs.py\n?? ../scripts/patch_chain_release.py",
|
|
23
|
-
"[2026-04-08] git-snapshot: .cairn/session.json | 29 ++++-\n .claude/settings.local.json | 47 ++++++-\n cli/.cairn/.hint-lock | 2 +-\n cli/.cairn/minify-map.json | 8 +-\n cli/.cairn/session.json | 32 ++++-\n cli/.cairn/views/62a614_bundle.js | 6 +-\n cli/lib/.cairn/minify-map.json | 6 +\n cli/lib/.cairn/views/fb78ac_upgrade.js | 37 +++++-\n cli/lib/.cairn/views/fc4a1a_add.js | 215 +++++++++++++++++++++++++--------\n 9 files changed, 316 insertions(+), 66 deletions(-) | status: M ../.cairn/session.json\n M ../.claude/settings.local.json\n M .cairn/.hint-lock\n M .cairn/minify-map.json\n M .cairn/session.json\n M .cairn/views/62a614_bundle.js\n M lib/.cairn/minify-map.json\n M lib/.cairn/views/fb78ac_upgrade.js\n M lib/.cairn/views/fc4a1a_add.js\n?? ../.cairn/.cairn-project\n?? ../.cairn/memory/\n?? ../.cairn/minify-map.json\n?? ../.cairn/views/\n?? .cairn/views/23edf4_sentinel_boss.py\n?? .cairn/views/7802b9_cicd_trigger.py\n?? .cairn/views/ac3df4_repo_task_engine.py\n?? lib/.cairn/views/2a85cc_init.js\n?? lib/.cairn/views/e26996_slack-setup.js\n?? ../scripts/fix_ask_codebase_context.py\n?? ../scripts/fix_ask_codebase_stdin.py\n?? ../scripts/fix_chain_slack.py\n?? ../scripts/fix_fstring.py\n?? ../scripts/fix_knowledge_cache.py\n?? ../scripts/fix_knowledge_cache_staleness.py\n?? ../scripts/fix_merge_confirm.py\n?? ../scripts/fix_permission_messages.py\n?? ../scripts/fix_pr_check_head_detect.py\n?? ../scripts/fix_pr_msg_newlines.py\n?? ../scripts/fix_pr_tracking_boss.py\n?? ../scripts/fix_pr_tracking_db.py\n?? ../scripts/fix_pr_tracking_main.py\n?? ../scripts/fix_project_isolation.py\n?? ../scripts/fix_system_prompt.py\n?? ../scripts/fix_two_bugs.py\n?? ../scripts/patch_chain_release.py"
|
|
23
|
+
"[2026-04-08] git-snapshot: .cairn/session.json | 29 ++++-\n .claude/settings.local.json | 47 ++++++-\n cli/.cairn/.hint-lock | 2 +-\n cli/.cairn/minify-map.json | 8 +-\n cli/.cairn/session.json | 32 ++++-\n cli/.cairn/views/62a614_bundle.js | 6 +-\n cli/lib/.cairn/minify-map.json | 6 +\n cli/lib/.cairn/views/fb78ac_upgrade.js | 37 +++++-\n cli/lib/.cairn/views/fc4a1a_add.js | 215 +++++++++++++++++++++++++--------\n 9 files changed, 316 insertions(+), 66 deletions(-) | status: M ../.cairn/session.json\n M ../.claude/settings.local.json\n M .cairn/.hint-lock\n M .cairn/minify-map.json\n M .cairn/session.json\n M .cairn/views/62a614_bundle.js\n M lib/.cairn/minify-map.json\n M lib/.cairn/views/fb78ac_upgrade.js\n M lib/.cairn/views/fc4a1a_add.js\n?? ../.cairn/.cairn-project\n?? ../.cairn/memory/\n?? ../.cairn/minify-map.json\n?? ../.cairn/views/\n?? .cairn/views/23edf4_sentinel_boss.py\n?? .cairn/views/7802b9_cicd_trigger.py\n?? .cairn/views/ac3df4_repo_task_engine.py\n?? lib/.cairn/views/2a85cc_init.js\n?? lib/.cairn/views/e26996_slack-setup.js\n?? ../scripts/fix_ask_codebase_context.py\n?? ../scripts/fix_ask_codebase_stdin.py\n?? ../scripts/fix_chain_slack.py\n?? ../scripts/fix_fstring.py\n?? ../scripts/fix_knowledge_cache.py\n?? ../scripts/fix_knowledge_cache_staleness.py\n?? ../scripts/fix_merge_confirm.py\n?? ../scripts/fix_permission_messages.py\n?? ../scripts/fix_pr_check_head_detect.py\n?? ../scripts/fix_pr_msg_newlines.py\n?? ../scripts/fix_pr_tracking_boss.py\n?? ../scripts/fix_pr_tracking_db.py\n?? ../scripts/fix_pr_tracking_main.py\n?? ../scripts/fix_project_isolation.py\n?? ../scripts/fix_system_prompt.py\n?? ../scripts/fix_two_bugs.py\n?? ../scripts/patch_chain_release.py",
|
|
24
|
+
"[2026-04-08] git-snapshot: .cairn/session.json | 29 ++++-\n .claude/settings.local.json | 47 ++++++-\n cli/.cairn/.hint-lock | 2 +-\n cli/.cairn/minify-map.json | 8 +-\n cli/.cairn/session.json | 33 ++++-\n cli/.cairn/views/62a614_bundle.js | 6 +-\n cli/lib/.cairn/minify-map.json | 6 +\n cli/lib/.cairn/views/fb78ac_upgrade.js | 37 +++++-\n cli/lib/.cairn/views/fc4a1a_add.js | 215 +++++++++++++++++++++++++--------\n cli/package.json | 2 +-\n sentinel/config_loader.py | 1 +\n 11 files changed, 319 insertions(+), 67 deletions(-) | status: M ../.cairn/session.json\n M ../.claude/settings.local.json\n M .cairn/.hint-lock\n M .cairn/minify-map.json\n M .cairn/session.json\n M .cairn/views/62a614_bundle.js\n M lib/.cairn/minify-map.json\n M lib/.cairn/views/fb78ac_upgrade.js\n M lib/.cairn/views/fc4a1a_add.js\n M package.json\n M ../sentinel/config_loader.py\n?? ../.cairn/.cairn-project\n?? ../.cairn/memory/\n?? ../.cairn/minify-map.json\n?? ../.cairn/views/\n?? .cairn/views/23edf4_sentinel_boss.py\n?? .cairn/views/7802b9_cicd_trigger.py\n?? .cairn/views/ac3df4_repo_task_engine.py\n?? lib/.cairn/views/2a85cc_init.js\n?? lib/.cairn/views/e26996_slack-setup.js\n?? ../scripts/fix_ask_codebase_context.py\n?? ../scripts/fix_ask_codebase_stdin.py\n?? ../scripts/fix_chain_slack.py\n?? ../scripts/fix_fstring.py\n?? ../scripts/fix_knowledge_cache.py\n?? ../scripts/fix_knowledge_cache_staleness.py\n?? ../scripts/fix_merge_confirm.py\n?? ../scripts/fix_permission_messages.py\n?? ../scripts/fix_pr_check_head_detect.py\n?? ../scripts/fix_pr_msg_newlines.py\n?? ../scripts/fix_pr_tracking_boss.py\n?? ../scripts/fix_pr_tracking_db.py\n?? ../scripts/fix_pr_tracking_main.py\n?? ../scripts/fix_project_isolation.py\n?? ../scripts/fix_system_prompt.py\n?? ../scripts/fix_two_bugs.py\n?? ../scripts/patch_chain_release.py",
|
|
25
|
+
"[2026-04-08] git-snapshot: .cairn/session.json | 29 ++++-\n .claude/settings.local.json | 47 ++++++-\n cli/.cairn/.hint-lock | 2 +-\n cli/.cairn/minify-map.json | 8 +-\n cli/.cairn/session.json | 34 +++++-\n cli/.cairn/views/62a614_bundle.js | 6 +-\n cli/lib/.cairn/minify-map.json | 6 +\n cli/lib/.cairn/views/fb78ac_upgrade.js | 37 +++++-\n cli/lib/.cairn/views/fc4a1a_add.js | 215 +++++++++++++++++++++++++--------\n cli/package.json | 2 +-\n sentinel/config_loader.py | 1 +\n 11 files changed, 320 insertions(+), 67 deletions(-) | status: M ../.cairn/session.json\n M ../.claude/settings.local.json\n M .cairn/.hint-lock\n M .cairn/minify-map.json\n M .cairn/session.json\n M .cairn/views/62a614_bundle.js\n M lib/.cairn/minify-map.json\n M lib/.cairn/views/fb78ac_upgrade.js\n M lib/.cairn/views/fc4a1a_add.js\n M package.json\n M ../sentinel/config_loader.py\n?? ../.cairn/.cairn-project\n?? ../.cairn/memory/\n?? ../.cairn/minify-map.json\n?? ../.cairn/views/\n?? .cairn/views/23edf4_sentinel_boss.py\n?? .cairn/views/7802b9_cicd_trigger.py\n?? .cairn/views/ac3df4_repo_task_engine.py\n?? lib/.cairn/views/2a85cc_init.js\n?? lib/.cairn/views/e26996_slack-setup.js\n?? ../scripts/fix_ask_codebase_context.py\n?? ../scripts/fix_ask_codebase_stdin.py\n?? ../scripts/fix_chain_slack.py\n?? ../scripts/fix_fstring.py\n?? ../scripts/fix_knowledge_cache.py\n?? ../scripts/fix_knowledge_cache_staleness.py\n?? ../scripts/fix_merge_confirm.py\n?? ../scripts/fix_permission_messages.py\n?? ../scripts/fix_pr_check_head_detect.py\n?? ../scripts/fix_pr_msg_newlines.py\n?? ../scripts/fix_pr_tracking_boss.py\n?? ../scripts/fix_pr_tracking_db.py\n?? ../scripts/fix_pr_tracking_main.py\n?? ../scripts/fix_project_isolation.py\n?? ../scripts/fix_system_prompt.py\n?? ../scripts/fix_two_bugs.py\n?? ../scripts/patch_chain_release.py",
|
|
26
|
+
"[2026-04-08] git-snapshot: .cairn/session.json | 29 ++++-\n .claude/settings.local.json | 47 ++++++-\n cli/.cairn/.hint-lock | 2 +-\n cli/.cairn/minify-map.json | 8 +-\n cli/.cairn/session.json | 35 +++++-\n cli/.cairn/views/62a614_bundle.js | 6 +-\n cli/lib/.cairn/minify-map.json | 6 +\n cli/lib/.cairn/views/fb78ac_upgrade.js | 37 +++++-\n cli/lib/.cairn/views/fc4a1a_add.js | 215 +++++++++++++++++++++++++--------\n cli/package.json | 2 +-\n sentinel/config_loader.py | 1 +\n 11 files changed, 321 insertions(+), 67 deletions(-) | status: M ../.cairn/session.json\n M ../.claude/settings.local.json\n M .cairn/.hint-lock\n M .cairn/minify-map.json\n M .cairn/session.json\n M .cairn/views/62a614_bundle.js\n M lib/.cairn/minify-map.json\n M lib/.cairn/views/fb78ac_upgrade.js\n M lib/.cairn/views/fc4a1a_add.js\n M package.json\n M ../sentinel/config_loader.py\n?? ../.cairn/.cairn-project\n?? ../.cairn/memory/\n?? ../.cairn/minify-map.json\n?? ../.cairn/views/\n?? .cairn/views/23edf4_sentinel_boss.py\n?? .cairn/views/7802b9_cicd_trigger.py\n?? .cairn/views/ac3df4_repo_task_engine.py\n?? lib/.cairn/views/2a85cc_init.js\n?? lib/.cairn/views/e26996_slack-setup.js\n?? ../scripts/fix_ask_codebase_context.py\n?? ../scripts/fix_ask_codebase_stdin.py\n?? ../scripts/fix_chain_slack.py\n?? ../scripts/fix_fstring.py\n?? ../scripts/fix_knowledge_cache.py\n?? ../scripts/fix_knowledge_cache_staleness.py\n?? ../scripts/fix_merge_confirm.py\n?? ../scripts/fix_permission_messages.py\n?? ../scripts/fix_pr_check_head_detect.py\n?? ../scripts/fix_pr_msg_newlines.py\n?? ../scripts/fix_pr_tracking_boss.py\n?? ../scripts/fix_pr_tracking_db.py\n?? ../scripts/fix_pr_tracking_main.py\n?? ../scripts/fix_project_isolation.py\n?? ../scripts/fix_system_prompt.py\n?? ../scripts/fix_two_bugs.py\n?? ../scripts/patch_chain_release.py",
|
|
27
|
+
"[2026-04-08] git-snapshot: .cairn/session.json | 29 ++++-\n .claude/settings.local.json | 47 ++++++-\n cli/.cairn/.hint-lock | 2 +-\n cli/.cairn/minify-map.json | 8 +-\n cli/.cairn/session.json | 36 +++++-\n cli/.cairn/views/62a614_bundle.js | 6 +-\n cli/lib/.cairn/minify-map.json | 6 +\n cli/lib/.cairn/views/fb78ac_upgrade.js | 37 +++++-\n cli/lib/.cairn/views/fc4a1a_add.js | 215 +++++++++++++++++++++++++--------\n cli/package.json | 2 +-\n sentinel/config_loader.py | 1 +\n 11 files changed, 322 insertions(+), 67 deletions(-) | status: M ../.cairn/session.json\n M ../.claude/settings.local.json\n M .cairn/.hint-lock\n M .cairn/minify-map.json\n M .cairn/session.json\n M .cairn/views/62a614_bundle.js\n M lib/.cairn/minify-map.json\n M lib/.cairn/views/fb78ac_upgrade.js\n M lib/.cairn/views/fc4a1a_add.js\n M package.json\n M ../sentinel/config_loader.py\n?? ../.cairn/.cairn-project\n?? ../.cairn/memory/\n?? ../.cairn/minify-map.json\n?? ../.cairn/views/\n?? .cairn/views/23edf4_sentinel_boss.py\n?? .cairn/views/7802b9_cicd_trigger.py\n?? .cairn/views/ac3df4_repo_task_engine.py\n?? lib/.cairn/views/2a85cc_init.js\n?? lib/.cairn/views/e26996_slack-setup.js\n?? ../scripts/fix_ask_codebase_context.py\n?? ../scripts/fix_ask_codebase_stdin.py\n?? ../scripts/fix_chain_slack.py\n?? ../scripts/fix_fstring.py\n?? ../scripts/fix_knowledge_cache.py\n?? ../scripts/fix_knowledge_cache_staleness.py\n?? ../scripts/fix_merge_confirm.py\n?? ../scripts/fix_permission_messages.py\n?? ../scripts/fix_pr_check_head_detect.py\n?? ../scripts/fix_pr_msg_newlines.py\n?? ../scripts/fix_pr_tracking_boss.py\n?? ../scripts/fix_pr_tracking_db.py\n?? ../scripts/fix_pr_tracking_main.py\n?? ../scripts/fix_project_isolation.py\n?? ../scripts/fix_system_prompt.py\n?? ../scripts/fix_two_bugs.py\n?? ../scripts/patch_chain_release.py",
|
|
28
|
+
"[2026-04-08] git-snapshot: .cairn/session.json | 29 ++++-\n .claude/settings.local.json | 47 ++++++-\n cli/.cairn/.hint-lock | 2 +-\n cli/.cairn/minify-map.json | 8 +-\n cli/.cairn/session.json | 37 +++++-\n cli/.cairn/views/62a614_bundle.js | 6 +-\n cli/lib/.cairn/minify-map.json | 6 +\n cli/lib/.cairn/views/fb78ac_upgrade.js | 37 +++++-\n cli/lib/.cairn/views/fc4a1a_add.js | 215 +++++++++++++++++++++++++--------\n cli/package.json | 2 +-\n sentinel/config_loader.py | 1 +\n 11 files changed, 323 insertions(+), 67 deletions(-) | status: M ../.cairn/session.json\n M ../.claude/settings.local.json\n M .cairn/.hint-lock\n M .cairn/minify-map.json\n M .cairn/session.json\n M .cairn/views/62a614_bundle.js\n M lib/.cairn/minify-map.json\n M lib/.cairn/views/fb78ac_upgrade.js\n M lib/.cairn/views/fc4a1a_add.js\n M package.json\n M ../sentinel/config_loader.py\n?? ../.cairn/.cairn-project\n?? ../.cairn/memory/\n?? ../.cairn/minify-map.json\n?? ../.cairn/views/\n?? .cairn/views/23edf4_sentinel_boss.py\n?? .cairn/views/7802b9_cicd_trigger.py\n?? .cairn/views/ac3df4_repo_task_engine.py\n?? lib/.cairn/views/2a85cc_init.js\n?? lib/.cairn/views/e26996_slack-setup.js\n?? ../scripts/fix_ask_codebase_context.py\n?? ../scripts/fix_ask_codebase_stdin.py\n?? ../scripts/fix_chain_slack.py\n?? ../scripts/fix_fstring.py\n?? ../scripts/fix_knowledge_cache.py\n?? ../scripts/fix_knowledge_cache_staleness.py\n?? ../scripts/fix_merge_confirm.py\n?? ../scripts/fix_permission_messages.py\n?? ../scripts/fix_pr_check_head_detect.py\n?? ../scripts/fix_pr_msg_newlines.py\n?? ../scripts/fix_pr_tracking_boss.py\n?? ../scripts/fix_pr_tracking_db.py\n?? ../scripts/fix_pr_tracking_main.py\n?? ../scripts/fix_project_isolation.py\n?? ../scripts/fix_system_prompt.py\n?? ../scripts/fix_two_bugs.py\n?? ../scripts/patch_chain_release.py",
|
|
29
|
+
"[2026-04-08] git-snapshot: .cairn/session.json | 29 ++++-\n .claude/settings.local.json | 47 ++++++-\n cli/.cairn/.hint-lock | 2 +-\n cli/.cairn/minify-map.json | 8 +-\n cli/.cairn/session.json | 38 +++++-\n cli/.cairn/views/62a614_bundle.js | 6 +-\n cli/lib/.cairn/minify-map.json | 6 +\n cli/lib/.cairn/views/fb78ac_upgrade.js | 37 +++++-\n cli/lib/.cairn/views/fc4a1a_add.js | 215 +++++++++++++++++++++++++--------\n cli/package.json | 2 +-\n sentinel/config_loader.py | 42 +++++--\n sentinel/dependency_manager.py | 4 +-\n sentinel/git_manager.py | 22 ++--\n sentinel/main.py | 48 +++++---\n sentinel/repo_task_engine.py | 18 ++-\n sentinel/sentinel_boss.py | 43 ++++---\n sentinel/state_store.py | 37 ++++++\n 17 files changed, 474 insertions(+), 130 deletions(-) | status: M ../.cairn/session.json\n M ../.claude/settings.local.json\n M .cairn/.hint-lock\n M .cairn/minify-map.json\n M .cairn/session.json\n M .cairn/views/62a614_bundle.js\n M lib/.cairn/minify-map.json\n M lib/.cairn/views/fb78ac_upgrade.js\n M lib/.cairn/views/fc4a1a_add.js\n M package.json\n M ../sentinel/config_loader.py\n M ../sentinel/dependency_manager.py\n M ../sentinel/git_manager.py\n M ../sentinel/main.py\n M ../sentinel/repo_task_engine.py\n M ../sentinel/sentinel_boss.py\n M ../sentinel/state_store.py\n?? ../.cairn/.cairn-project\n?? ../.cairn/memory/\n?? ../.cairn/minify-map.json\n?? ../.cairn/views/\n?? .cairn/views/23edf4_sentinel_boss.py\n?? .cairn/views/7802b9_cicd_trigger.py\n?? .cairn/views/ac3df4_repo_task_engine.py\n?? lib/.cairn/views/2a85cc_init.js\n?? lib/.cairn/views/e26996_slack-setup.js\n?? ../scripts/fix_ask_codebase_context.py\n?? ../scripts/fix_ask_codebase_stdin.py\n?? ../scripts/fix_chain_slack.py\n?? ../scripts/fix_fstring.py\n?? ../scripts/fix_knowledge_cache.py\n?? ../scripts/fix_knowledge_cache_staleness.py\n?? ../scripts/fix_merge_confirm.py\n?? ../scripts/fix_permission_messages.py\n?? ../scripts/fix_pr_check_head_detect.py\n?? ../scripts/fix_pr_msg_newlines.py\n?? ../scripts/fix_pr_tracking_boss.py\n?? ../scripts/fix_pr_tracking_db.py\n?? ../scripts/fix_pr_tracking_main.py\n?? ../scripts/fix_project_isolation.py\n?? ../scripts/fix_system_prompt.py\n?? ../scripts/fix_two_bugs.py\n?? ../scripts/patch_chain_release.py",
|
|
30
|
+
"[2026-04-08] git-snapshot: .cairn/session.json | 29 ++++-\n .claude/settings.local.json | 47 ++++++-\n cli/.cairn/.hint-lock | 2 +-\n cli/.cairn/minify-map.json | 8 +-\n cli/.cairn/session.json | 39 +++++-\n cli/.cairn/views/62a614_bundle.js | 6 +-\n cli/lib/.cairn/minify-map.json | 6 +\n cli/lib/.cairn/views/fb78ac_upgrade.js | 37 +++++-\n cli/lib/.cairn/views/fc4a1a_add.js | 215 +++++++++++++++++++++++++--------\n cli/package.json | 2 +-\n sentinel/config_loader.py | 42 +++++--\n sentinel/dependency_manager.py | 4 +-\n sentinel/git_manager.py | 22 ++--\n sentinel/main.py | 48 +++++---\n sentinel/repo_task_engine.py | 18 ++-\n sentinel/sentinel_boss.py | 43 ++++---\n sentinel/state_store.py | 37 ++++++\n 17 files changed, 475 insertions(+), 130 deletions(-) | status: M ../.cairn/session.json\n M ../.claude/settings.local.json\n M .cairn/.hint-lock\n M .cairn/minify-map.json\n M .cairn/session.json\n M .cairn/views/62a614_bundle.js\n M lib/.cairn/minify-map.json\n M lib/.cairn/views/fb78ac_upgrade.js\n M lib/.cairn/views/fc4a1a_add.js\n M package.json\n M ../sentinel/config_loader.py\n M ../sentinel/dependency_manager.py\n M ../sentinel/git_manager.py\n M ../sentinel/main.py\n M ../sentinel/repo_task_engine.py\n M ../sentinel/sentinel_boss.py\n M ../sentinel/state_store.py\n?? ../.cairn/.cairn-project\n?? ../.cairn/memory/\n?? ../.cairn/minify-map.json\n?? ../.cairn/views/\n?? .cairn/views/23edf4_sentinel_boss.py\n?? .cairn/views/7802b9_cicd_trigger.py\n?? .cairn/views/ac3df4_repo_task_engine.py\n?? lib/.cairn/views/2a85cc_init.js\n?? lib/.cairn/views/e26996_slack-setup.js\n?? ../scripts/fix_ask_codebase_context.py\n?? ../scripts/fix_ask_codebase_stdin.py\n?? ../scripts/fix_chain_slack.py\n?? ../scripts/fix_fstring.py\n?? ../scripts/fix_knowledge_cache.py\n?? ../scripts/fix_knowledge_cache_staleness.py\n?? ../scripts/fix_merge_confirm.py\n?? ../scripts/fix_permission_messages.py\n?? ../scripts/fix_pr_check_head_detect.py\n?? ../scripts/fix_pr_msg_newlines.py\n?? ../scripts/fix_pr_tracking_boss.py\n?? ../scripts/fix_pr_tracking_db.py\n?? ../scripts/fix_pr_tracking_main.py\n?? ../scripts/fix_project_isolation.py\n?? ../scripts/fix_system_prompt.py\n?? ../scripts/fix_two_bugs.py\n?? ../scripts/patch_chain_release.py",
|
|
31
|
+
"[2026-04-08] git-snapshot: .cairn/session.json | 29 ++++-\n .claude/settings.local.json | 47 ++++++-\n cli/.cairn/.hint-lock | 2 +-\n cli/.cairn/minify-map.json | 8 +-\n cli/.cairn/session.json | 40 +++++-\n cli/.cairn/views/62a614_bundle.js | 6 +-\n cli/lib/.cairn/minify-map.json | 6 +\n cli/lib/.cairn/views/fb78ac_upgrade.js | 37 +++++-\n cli/lib/.cairn/views/fc4a1a_add.js | 215 +++++++++++++++++++++++++--------\n cli/package.json | 2 +-\n sentinel/config_loader.py | 42 +++++--\n sentinel/dependency_manager.py | 4 +-\n sentinel/git_manager.py | 22 ++--\n sentinel/main.py | 48 +++++---\n sentinel/repo_task_engine.py | 18 ++-\n sentinel/sentinel_boss.py | 43 ++++---\n sentinel/state_store.py | 37 ++++++\n 17 files changed, 476 insertions(+), 130 deletions(-) | status: M ../.cairn/session.json\n M ../.claude/settings.local.json\n M .cairn/.hint-lock\n M .cairn/minify-map.json\n M .cairn/session.json\n M .cairn/views/62a614_bundle.js\n M lib/.cairn/minify-map.json\n M lib/.cairn/views/fb78ac_upgrade.js\n M lib/.cairn/views/fc4a1a_add.js\n M package.json\n M ../sentinel/config_loader.py\n M ../sentinel/dependency_manager.py\n M ../sentinel/git_manager.py\n M ../sentinel/main.py\n M ../sentinel/repo_task_engine.py\n M ../sentinel/sentinel_boss.py\n M ../sentinel/state_store.py\n?? ../.cairn/.cairn-project\n?? ../.cairn/memory/\n?? ../.cairn/minify-map.json\n?? ../.cairn/views/\n?? .cairn/views/23edf4_sentinel_boss.py\n?? .cairn/views/7802b9_cicd_trigger.py\n?? .cairn/views/ac3df4_repo_task_engine.py\n?? lib/.cairn/views/2a85cc_init.js\n?? lib/.cairn/views/e26996_slack-setup.js\n?? ../scripts/fix_ask_codebase_context.py\n?? ../scripts/fix_ask_codebase_stdin.py\n?? ../scripts/fix_chain_slack.py\n?? ../scripts/fix_fstring.py\n?? ../scripts/fix_knowledge_cache.py\n?? ../scripts/fix_knowledge_cache_staleness.py\n?? ../scripts/fix_merge_confirm.py\n?? ../scripts/fix_permission_messages.py\n?? ../scripts/fix_pr_check_head_detect.py\n?? ../scripts/fix_pr_msg_newlines.py\n?? ../scripts/fix_pr_tracking_boss.py\n?? ../scripts/fix_pr_tracking_db.py\n?? ../scripts/fix_pr_tracking_main.py\n?? ../scripts/fix_project_isolation.py\n?? ../scripts/fix_system_prompt.py\n?? ../scripts/fix_two_bugs.py\n?? ../scripts/patch_chain_release.py"
|
|
24
32
|
],
|
|
25
33
|
"mtime_snapshot": {
|
|
26
34
|
"J:\\Projects\\Sentinel\\cli\\bin\\sentinel.js": 1774252515044.4768,
|
package/package.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
__version__ = "1.5.
|
|
1
|
+
__version__ = "1.5.18"
|
|
@@ -6,6 +6,7 @@ from __future__ import annotations
|
|
|
6
6
|
import logging
|
|
7
7
|
import os
|
|
8
8
|
import signal
|
|
9
|
+
import subprocess
|
|
9
10
|
from dataclasses import dataclass, field
|
|
10
11
|
from pathlib import Path
|
|
11
12
|
from typing import Optional
|
|
@@ -81,7 +82,8 @@ class SentinelConfig:
|
|
|
81
82
|
sentinel_dev_repo_path: str = "" # path to Sentinel source repo for Dev Claude
|
|
82
83
|
sentinel_dev_soak_minutes: int = 30 # minutes to monitor after a Patch restart before publishing
|
|
83
84
|
sentinel_dev_auto_publish: bool = False # if True, auto-publish to npm after clean soak
|
|
84
|
-
|
|
85
|
+
auto_commit: bool = False # project-level default: push directly to main (no PR); repos can override
|
|
86
|
+
auto_release: bool = False # project-level default: trigger CI/CD pipeline after push; repos can override
|
|
85
87
|
|
|
86
88
|
|
|
87
89
|
@dataclass
|
|
@@ -110,7 +112,8 @@ class RepoConfig:
|
|
|
110
112
|
repo_url: str = ""
|
|
111
113
|
local_path: str = ""
|
|
112
114
|
branch: str = "main"
|
|
113
|
-
|
|
115
|
+
auto_commit: bool | None = None # None = inherit from SentinelConfig.auto_commit; explicit True/False overrides
|
|
116
|
+
auto_release: bool | None = None # None = inherit from SentinelConfig.auto_release; explicit True/False overrides
|
|
114
117
|
cicd_type: str = ""
|
|
115
118
|
cicd_job_url: str = ""
|
|
116
119
|
cicd_user: str = "" # Jenkins username for Basic auth (defaults to "sentinel")
|
|
@@ -125,6 +128,39 @@ class RepoConfig:
|
|
|
125
128
|
return self.git_ssh_deploy_key or self.git_ssh_user_key
|
|
126
129
|
|
|
127
130
|
|
|
131
|
+
# ── Branch detection ──────────────────────────────────────────────────────────
|
|
132
|
+
|
|
133
|
+
def _detect_default_branch(local_path: str) -> str:
|
|
134
|
+
"""
|
|
135
|
+
Detect the remote default branch for an already-cloned repo.
|
|
136
|
+
1. Try local symbolic-ref (fast, no network — valid after any fetch).
|
|
137
|
+
2. Fall back to 'git remote show origin' (network call, authoritative).
|
|
138
|
+
3. Return 'main' as last resort.
|
|
139
|
+
"""
|
|
140
|
+
try:
|
|
141
|
+
r = subprocess.run(
|
|
142
|
+
["git", "symbolic-ref", "refs/remotes/origin/HEAD", "--short"],
|
|
143
|
+
cwd=local_path, capture_output=True, text=True, timeout=5,
|
|
144
|
+
)
|
|
145
|
+
if r.returncode == 0:
|
|
146
|
+
ref = r.stdout.strip() # e.g. "origin/main" or "origin/master"
|
|
147
|
+
return ref.split("/", 1)[1] if "/" in ref else ref
|
|
148
|
+
except Exception:
|
|
149
|
+
pass
|
|
150
|
+
try:
|
|
151
|
+
r = subprocess.run(
|
|
152
|
+
["git", "remote", "show", "origin"],
|
|
153
|
+
cwd=local_path, capture_output=True, text=True, timeout=15,
|
|
154
|
+
)
|
|
155
|
+
if r.returncode == 0:
|
|
156
|
+
for line in r.stdout.splitlines():
|
|
157
|
+
if "HEAD branch:" in line:
|
|
158
|
+
return line.split(":", 1)[1].strip()
|
|
159
|
+
except Exception:
|
|
160
|
+
pass
|
|
161
|
+
return "main"
|
|
162
|
+
|
|
163
|
+
|
|
128
164
|
# ── Loader ────────────────────────────────────────────────────────────────────
|
|
129
165
|
|
|
130
166
|
class ConfigLoader:
|
|
@@ -239,8 +275,10 @@ class ConfigLoader:
|
|
|
239
275
|
c.sentinel_dev_repo_path = d.get("SENTINEL_DEV_REPO_PATH", "")
|
|
240
276
|
c.sentinel_dev_soak_minutes = int(d.get("SENTINEL_DEV_SOAK_MINUTES", 30))
|
|
241
277
|
c.sentinel_dev_auto_publish = d.get("SENTINEL_DEV_AUTO_PUBLISH", "false").lower() == "true"
|
|
242
|
-
if "
|
|
243
|
-
c.
|
|
278
|
+
if "AUTO_COMMIT" in d:
|
|
279
|
+
c.auto_commit = d["AUTO_COMMIT"].lower() == "true"
|
|
280
|
+
if "AUTO_RELEASE" in d:
|
|
281
|
+
c.auto_release = d["AUTO_RELEASE"].lower() == "true"
|
|
244
282
|
self.sentinel = c
|
|
245
283
|
|
|
246
284
|
def _load_log_sources(self):
|
|
@@ -289,8 +327,14 @@ class ConfigLoader:
|
|
|
289
327
|
r.repo_name = path.stem
|
|
290
328
|
r.repo_url = d.get("REPO_URL", "")
|
|
291
329
|
r.local_path = str(Path(self.config_dir).parent / "repos" / r.repo_name)
|
|
292
|
-
|
|
293
|
-
|
|
330
|
+
if "BRANCH" in d:
|
|
331
|
+
r.branch = d["BRANCH"]
|
|
332
|
+
elif Path(r.local_path).is_dir():
|
|
333
|
+
r.branch = _detect_default_branch(r.local_path)
|
|
334
|
+
else:
|
|
335
|
+
r.branch = "main" # fallback until repo is cloned by sentinel init
|
|
336
|
+
r.auto_commit = bool(d["AUTO_COMMIT"].lower() == "true") if "AUTO_COMMIT" in d else None
|
|
337
|
+
r.auto_release = bool(d["AUTO_RELEASE"].lower() == "true") if "AUTO_RELEASE" in d else None
|
|
294
338
|
r.cicd_type = d.get("CICD_TYPE", "")
|
|
295
339
|
r.cicd_job_url = d.get("CICD_JOB_URL", "")
|
|
296
340
|
r.cicd_user = d.get("CICD_USER", "")
|
|
@@ -318,15 +362,25 @@ class ConfigLoader:
|
|
|
318
362
|
self.load()
|
|
319
363
|
|
|
320
364
|
|
|
321
|
-
def
|
|
365
|
+
def resolve_auto_commit(repo: "RepoConfig", sentinel: "SentinelConfig") -> bool:
|
|
322
366
|
"""
|
|
323
|
-
Three-level
|
|
324
|
-
1. Repo-level explicit value (if
|
|
325
|
-
2. Project-level default (
|
|
367
|
+
Three-level AUTO_COMMIT resolution:
|
|
368
|
+
1. Repo-level explicit value (if AUTO_COMMIT is set in the repo .properties file)
|
|
369
|
+
2. Project-level default (AUTO_COMMIT in sentinel.properties)
|
|
326
370
|
3. System default: False
|
|
371
|
+
"""
|
|
372
|
+
if repo.auto_commit is not None:
|
|
373
|
+
return repo.auto_commit
|
|
374
|
+
return sentinel.auto_commit
|
|
327
375
|
|
|
328
|
-
|
|
376
|
+
|
|
377
|
+
def resolve_auto_release(repo: "RepoConfig", sentinel: "SentinelConfig") -> bool:
|
|
378
|
+
"""
|
|
379
|
+
Three-level AUTO_RELEASE resolution:
|
|
380
|
+
1. Repo-level explicit value (if AUTO_RELEASE is set in the repo .properties file)
|
|
381
|
+
2. Project-level default (AUTO_RELEASE in sentinel.properties)
|
|
382
|
+
3. System default: False
|
|
329
383
|
"""
|
|
330
|
-
if repo.
|
|
331
|
-
return repo.
|
|
332
|
-
return sentinel.
|
|
384
|
+
if repo.auto_release is not None:
|
|
385
|
+
return repo.auto_release
|
|
386
|
+
return sentinel.auto_release
|
|
@@ -275,8 +275,8 @@ def plan_cascade(
|
|
|
275
275
|
{
|
|
276
276
|
"repo": r.repo_name,
|
|
277
277
|
"current_version": v,
|
|
278
|
-
"
|
|
279
|
-
"action": "push to main" if r.
|
|
278
|
+
"auto_commit": r.auto_commit,
|
|
279
|
+
"action": "push to main" if r.auto_commit is True else ("open PR" if r.auto_commit is False else "inherits project AUTO_COMMIT"),
|
|
280
280
|
}
|
|
281
281
|
for r, v in all_dependents
|
|
282
282
|
],
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"""
|
|
2
2
|
git_manager.py — Git operations and GitHub PR creation.
|
|
3
3
|
|
|
4
|
-
Supports two modes (driven by repo.
|
|
4
|
+
Supports two modes (driven by repo.auto_commit):
|
|
5
5
|
True → apply fix directly to main branch and push
|
|
6
6
|
False → push to sentinel/fix-<fp> branch and open a GitHub PR
|
|
7
7
|
"""
|
|
@@ -15,7 +15,7 @@ from pathlib import Path
|
|
|
15
15
|
|
|
16
16
|
import requests
|
|
17
17
|
|
|
18
|
-
from .config_loader import RepoConfig, SentinelConfig,
|
|
18
|
+
from .config_loader import RepoConfig, SentinelConfig, resolve_auto_commit
|
|
19
19
|
from .log_parser import ErrorEvent
|
|
20
20
|
|
|
21
21
|
logger = logging.getLogger(__name__)
|
|
@@ -251,16 +251,16 @@ def push_dep_update(
|
|
|
251
251
|
commit_hash: str,
|
|
252
252
|
) -> tuple[str, str, bool]:
|
|
253
253
|
"""
|
|
254
|
-
Push a dependency update commit.
|
|
255
|
-
|
|
254
|
+
Push a dependency update commit. AUTO_COMMIT=true → main branch.
|
|
255
|
+
AUTO_COMMIT=false → sentinel/dep-<artifact>-<version> branch + open PR.
|
|
256
256
|
Returns (branch, pr_url, push_ok).
|
|
257
257
|
push_ok=False means the commit is local only (no write access) — chain release continues.
|
|
258
258
|
"""
|
|
259
259
|
env = _git_env(repo)
|
|
260
260
|
local_path = repo.local_path
|
|
261
|
-
|
|
261
|
+
auto_commit = resolve_auto_commit(repo, cfg)
|
|
262
262
|
|
|
263
|
-
if
|
|
263
|
+
if auto_commit:
|
|
264
264
|
r = _git(["push", "origin", repo.branch], cwd=local_path, env=env)
|
|
265
265
|
if r.returncode != 0:
|
|
266
266
|
logger.warning(
|
|
@@ -348,16 +348,16 @@ def publish(
|
|
|
348
348
|
commit_hash: str,
|
|
349
349
|
) -> tuple[str, str]:
|
|
350
350
|
"""
|
|
351
|
-
Push the fix and (if
|
|
351
|
+
Push the fix and (if AUTO_COMMIT=false) open a PR.
|
|
352
352
|
|
|
353
353
|
Returns:
|
|
354
|
-
(branch, pr_url) — pr_url is "" when
|
|
354
|
+
(branch, pr_url) — pr_url is "" when AUTO_COMMIT=true
|
|
355
355
|
"""
|
|
356
356
|
env = _git_env(repo)
|
|
357
357
|
local_path = repo.local_path
|
|
358
|
-
|
|
358
|
+
auto_commit = resolve_auto_commit(repo, cfg)
|
|
359
359
|
|
|
360
|
-
if not
|
|
360
|
+
if not auto_commit:
|
|
361
361
|
if remote_fix_exists(repo, event.fingerprint, cfg):
|
|
362
362
|
logger.info(
|
|
363
363
|
"Remote fix branch already exists for %s — skipping duplicate push",
|
|
@@ -365,7 +365,7 @@ def publish(
|
|
|
365
365
|
)
|
|
366
366
|
return "", ""
|
|
367
367
|
|
|
368
|
-
if
|
|
368
|
+
if auto_commit:
|
|
369
369
|
r = _git(["push", "origin", repo.branch], cwd=local_path, env=env)
|
|
370
370
|
if r.returncode != 0:
|
|
371
371
|
logger.error("git push failed:\n%s", r.stderr)
|
package/python/sentinel/main.py
CHANGED
|
@@ -21,7 +21,7 @@ from datetime import datetime, timezone
|
|
|
21
21
|
from pathlib import Path
|
|
22
22
|
|
|
23
23
|
from .cairn_client import ensure_installed as cairn_installed, index_repo
|
|
24
|
-
from .config_loader import ConfigLoader, SentinelConfig,
|
|
24
|
+
from .config_loader import ConfigLoader, SentinelConfig, resolve_auto_commit, resolve_auto_release
|
|
25
25
|
from .fix_engine import generate_fix
|
|
26
26
|
from .git_manager import apply_and_commit, publish, _git_env, MissingToolError, MavenAuthError, poll_open_prs
|
|
27
27
|
from .cicd_trigger import trigger as cicd_trigger
|
|
@@ -183,7 +183,8 @@ async def _handle_error(event: ErrorEvent, cfg_loader: ConfigLoader, store: Stat
|
|
|
183
183
|
if not repo:
|
|
184
184
|
return
|
|
185
185
|
|
|
186
|
-
|
|
186
|
+
auto_commit = resolve_auto_commit(repo, sentinel)
|
|
187
|
+
auto_release = resolve_auto_release(repo, sentinel)
|
|
187
188
|
|
|
188
189
|
# ── Dismissed errors — skip silently (human explicitly said so) ───────────
|
|
189
190
|
if store.is_dismissed(event.fingerprint):
|
|
@@ -195,8 +196,8 @@ async def _handle_error(event: ErrorEvent, cfg_loader: ConfigLoader, store: Stat
|
|
|
195
196
|
return
|
|
196
197
|
|
|
197
198
|
# ── Determine what we can do before alerting ──────────────────────────────
|
|
198
|
-
cannot_fix = event.is_infra_issue or (event.severity == "CRITICAL" and
|
|
199
|
-
fix_status = "cannot_fix" if cannot_fix else ("will_fix_pr" if not
|
|
199
|
+
cannot_fix = event.is_infra_issue or (event.severity == "CRITICAL" and auto_commit)
|
|
200
|
+
fix_status = "cannot_fix" if cannot_fix else ("will_fix_pr" if not auto_commit else "attempting")
|
|
200
201
|
|
|
201
202
|
stack_lines = getattr(event, "stack_trace", None)
|
|
202
203
|
stack_preview = ""
|
|
@@ -229,8 +230,8 @@ async def _handle_error(event: ErrorEvent, cfg_loader: ConfigLoader, store: Stat
|
|
|
229
230
|
store.record_fix(event.fingerprint, "skipped", repo_name=repo.repo_name)
|
|
230
231
|
return
|
|
231
232
|
|
|
232
|
-
if event.severity == "CRITICAL" and
|
|
233
|
-
logger.warning("CRITICAL in auto-
|
|
233
|
+
if event.severity == "CRITICAL" and auto_commit:
|
|
234
|
+
logger.warning("CRITICAL in auto-commit repo '%s' — alerted, human review needed", repo.repo_name)
|
|
234
235
|
store.record_fix(event.fingerprint, "skipped", repo_name=repo.repo_name)
|
|
235
236
|
return
|
|
236
237
|
|
|
@@ -305,7 +306,7 @@ async def _handle_error(event: ErrorEvent, cfg_loader: ConfigLoader, store: Stat
|
|
|
305
306
|
branch, pr_url = publish(event, repo, sentinel, commit_hash)
|
|
306
307
|
store.record_fix(
|
|
307
308
|
event.fingerprint,
|
|
308
|
-
"applied" if
|
|
309
|
+
"applied" if auto_commit else "pending",
|
|
309
310
|
patch_path=str(patch_path),
|
|
310
311
|
commit_hash=commit_hash,
|
|
311
312
|
branch=branch,
|
|
@@ -318,6 +319,10 @@ async def _handle_error(event: ErrorEvent, cfg_loader: ConfigLoader, store: Stat
|
|
|
318
319
|
_progress(f":white_check_mark: Fix committed — PR opened: {pr_url}")
|
|
319
320
|
else:
|
|
320
321
|
_progress(f":white_check_mark: Fix pushed to `{branch}` (`{commit_hash[:8]}`)")
|
|
322
|
+
if auto_commit and not auto_release:
|
|
323
|
+
store.add_pending_release(repo.repo_name, commit_hash, branch,
|
|
324
|
+
description=event.message[:120],
|
|
325
|
+
fingerprint=event.fingerprint)
|
|
321
326
|
|
|
322
327
|
send_fix_notification(sentinel, {
|
|
323
328
|
"source": event.source,
|
|
@@ -330,11 +335,11 @@ async def _handle_error(event: ErrorEvent, cfg_loader: ConfigLoader, store: Stat
|
|
|
330
335
|
"commit_hash": commit_hash,
|
|
331
336
|
"branch": branch,
|
|
332
337
|
"pr_url": pr_url,
|
|
333
|
-
"
|
|
338
|
+
"auto_commit": auto_commit,
|
|
334
339
|
"files_changed": [],
|
|
335
340
|
})
|
|
336
341
|
|
|
337
|
-
if
|
|
342
|
+
if auto_commit and auto_release:
|
|
338
343
|
ok = cicd_trigger(repo, store, event.fingerprint)
|
|
339
344
|
if ok and repo.cicd_type.lower() in ("jenkins_release", "jenkins-release"):
|
|
340
345
|
_run_cascade(repo, sentinel, cfg_loader)
|
|
@@ -375,7 +380,8 @@ async def _handle_issue(event: IssueEvent, cfg_loader: ConfigLoader, store: Stat
|
|
|
375
380
|
)
|
|
376
381
|
return # Leave the file so admin can add the header
|
|
377
382
|
|
|
378
|
-
|
|
383
|
+
auto_commit = resolve_auto_commit(repo, sentinel)
|
|
384
|
+
auto_release = resolve_auto_release(repo, sentinel)
|
|
379
385
|
|
|
380
386
|
# Post "working on" to channel and capture thread_ts for progress replies
|
|
381
387
|
from .notify import slack_alert as _slack_alert, slack_thread_reply as _slack_reply
|
|
@@ -438,7 +444,7 @@ async def _handle_issue(event: IssueEvent, cfg_loader: ConfigLoader, store: Stat
|
|
|
438
444
|
branch, pr_url = publish(event, repo, sentinel, commit_hash)
|
|
439
445
|
store.record_fix(
|
|
440
446
|
event.fingerprint,
|
|
441
|
-
"applied" if
|
|
447
|
+
"applied" if auto_commit else "pending",
|
|
442
448
|
patch_path=str(patch_path),
|
|
443
449
|
commit_hash=commit_hash,
|
|
444
450
|
branch=branch,
|
|
@@ -457,17 +463,22 @@ async def _handle_issue(event: IssueEvent, cfg_loader: ConfigLoader, store: Stat
|
|
|
457
463
|
"commit_hash": commit_hash,
|
|
458
464
|
"branch": branch,
|
|
459
465
|
"pr_url": pr_url,
|
|
460
|
-
"
|
|
466
|
+
"auto_commit": auto_commit,
|
|
461
467
|
"files_changed": [],
|
|
462
468
|
})
|
|
463
469
|
if pr_url:
|
|
464
470
|
_progress(f":arrow_right: <{pr_url}|PR opened> — awaiting review")
|
|
471
|
+
else:
|
|
472
|
+
if auto_commit and not auto_release:
|
|
473
|
+
store.add_pending_release(repo.repo_name, commit_hash, branch,
|
|
474
|
+
description=event.message[:120],
|
|
475
|
+
fingerprint=event.fingerprint)
|
|
465
476
|
notify_fix_applied(sentinel, event.source, event.message,
|
|
466
477
|
repo_name=repo.repo_name, branch=branch, pr_url=pr_url,
|
|
467
478
|
submitter_user_id=submitter_uid, origin_channel=_origin_channel)
|
|
468
479
|
mark_done(event.issue_file)
|
|
469
480
|
|
|
470
|
-
if
|
|
481
|
+
if auto_commit and auto_release:
|
|
471
482
|
ok = cicd_trigger(repo, store, event.fingerprint)
|
|
472
483
|
if ok:
|
|
473
484
|
_progress(f":rocket: Release triggered via {repo.cicd_type}")
|
|
@@ -529,7 +540,7 @@ async def _handle_issue(event: IssueEvent, cfg_loader: ConfigLoader, store: Stat
|
|
|
529
540
|
branch, pr_url = publish(event, repo, sentinel, commit_hash)
|
|
530
541
|
store.record_fix(
|
|
531
542
|
event.fingerprint,
|
|
532
|
-
"applied" if
|
|
543
|
+
"applied" if auto_commit else "pending",
|
|
533
544
|
patch_path=str(patch_path), commit_hash=commit_hash,
|
|
534
545
|
branch=branch, pr_url=pr_url, repo_name=repo.repo_name, sentinel_marker=marker,
|
|
535
546
|
)
|
|
@@ -539,15 +550,20 @@ async def _handle_issue(event: IssueEvent, cfg_loader: ConfigLoader, store: Stat
|
|
|
539
550
|
"message": event.message, "stack_trace": event.body,
|
|
540
551
|
"repo_name": repo.repo_name, "commit_hash": commit_hash,
|
|
541
552
|
"branch": branch, "pr_url": pr_url,
|
|
542
|
-
"
|
|
553
|
+
"auto_commit": auto_commit, "files_changed": [],
|
|
543
554
|
})
|
|
544
555
|
if pr_url:
|
|
545
556
|
_progress(f":arrow_right: <{pr_url}|PR opened> — awaiting review")
|
|
557
|
+
else:
|
|
558
|
+
if auto_commit and not auto_release:
|
|
559
|
+
store.add_pending_release(repo.repo_name, commit_hash, branch,
|
|
560
|
+
description=event.message[:120],
|
|
561
|
+
fingerprint=event.fingerprint)
|
|
546
562
|
notify_fix_applied(sentinel, event.source, event.message,
|
|
547
563
|
repo_name=repo.repo_name, branch=branch, pr_url=pr_url,
|
|
548
564
|
submitter_user_id=submitter_uid, origin_channel=_origin_channel)
|
|
549
565
|
mark_done(event.issue_file)
|
|
550
|
-
if
|
|
566
|
+
if auto_commit and auto_release:
|
|
551
567
|
ok = cicd_trigger(repo, store, event.fingerprint)
|
|
552
568
|
if ok:
|
|
553
569
|
_progress(f":rocket: Release triggered via {repo.cicd_type}")
|
|
@@ -28,7 +28,7 @@ from dataclasses import dataclass, field
|
|
|
28
28
|
from datetime import datetime, timezone
|
|
29
29
|
from pathlib import Path
|
|
30
30
|
|
|
31
|
-
from .config_loader import RepoConfig, SentinelConfig,
|
|
31
|
+
from .config_loader import RepoConfig, SentinelConfig, resolve_auto_commit, resolve_auto_release
|
|
32
32
|
from .fix_engine import _claude_cmd, _run_claude_attempt, _is_auth_error, _progress_from_line
|
|
33
33
|
from .git_manager import _git_env
|
|
34
34
|
|
|
@@ -276,12 +276,13 @@ def run_repo_task(
|
|
|
276
276
|
return "skip", "Claude completed but made no changes to the codebase."
|
|
277
277
|
|
|
278
278
|
commit_hash = after_hash
|
|
279
|
-
|
|
279
|
+
auto_commit = resolve_auto_commit(repo, cfg)
|
|
280
|
+
auto_release = resolve_auto_release(repo, cfg)
|
|
280
281
|
|
|
281
282
|
if on_progress:
|
|
282
283
|
on_progress(f":arrow_up: Pushing to `{repo.repo_name}`...")
|
|
283
284
|
|
|
284
|
-
if
|
|
285
|
+
if auto_commit:
|
|
285
286
|
r = subprocess.run(
|
|
286
287
|
["git", "push", "origin", repo.branch],
|
|
287
288
|
cwd=local_path, capture_output=True, text=True, env=env, timeout=60,
|
|
@@ -290,7 +291,16 @@ def run_repo_task(
|
|
|
290
291
|
logger.error("git push failed for %s: %s", repo.repo_name, r.stderr[:300])
|
|
291
292
|
return "error", f"git push failed: {r.stderr.strip()[:200]}"
|
|
292
293
|
logger.info("Repo task: pushed to %s/%s sha=%s", repo.repo_name, repo.branch, commit_hash[:8])
|
|
293
|
-
if
|
|
294
|
+
if store and not auto_release:
|
|
295
|
+
try:
|
|
296
|
+
store.add_pending_release(
|
|
297
|
+
repo.repo_name, commit_hash, repo.branch,
|
|
298
|
+
description=getattr(task, "description", "")[:120],
|
|
299
|
+
fingerprint=task.fingerprint,
|
|
300
|
+
)
|
|
301
|
+
except Exception as exc:
|
|
302
|
+
logger.debug("Could not record pending release: %s", exc)
|
|
303
|
+
if repo.cicd_type and auto_release:
|
|
294
304
|
try:
|
|
295
305
|
from .cicd_trigger import trigger as cicd_trigger
|
|
296
306
|
ok = cicd_trigger(repo, None, task.fingerprint)
|
|
@@ -40,7 +40,7 @@ _SYSTEM = """\
|
|
|
40
40
|
You are Sentinel Boss — the AI interface for Sentinel, a 24/7 autonomous DevOps agent.
|
|
41
41
|
|
|
42
42
|
Sentinel watches production logs, detects errors, generates code fixes via Claude Code,
|
|
43
|
-
and opens GitHub PRs for admin review (or pushes directly if
|
|
43
|
+
and opens GitHub PRs for admin review (or pushes directly if AUTO_COMMIT=true).
|
|
44
44
|
|
|
45
45
|
Your job:
|
|
46
46
|
- Understand what the DevOps engineer needs in natural language
|
|
@@ -372,7 +372,7 @@ Common usage questions and answers:
|
|
|
372
372
|
Q: How do I ask you to fix a bug?
|
|
373
373
|
A: Just describe it in plain language: "fix the NullPointerException in OrderService".
|
|
374
374
|
Sentinel will classify the error, find the right repo, and open a PR (or push directly
|
|
375
|
-
if
|
|
375
|
+
if AUTO_COMMIT=true).
|
|
376
376
|
|
|
377
377
|
Q: How do I review and merge a fix PR?
|
|
378
378
|
A: Say "list pending PRs" to see open Sentinel fix PRs. Then "merge the fix for TypeLib"
|
|
@@ -412,9 +412,11 @@ A: After every fix, Sentinel injects a SENTINEL:#<fingerprint> marker into each
|
|
|
412
412
|
method. When that marker appears in production logs, a quiet period starts. After
|
|
413
413
|
MARKER_CONFIRM_HOURS with no recurrence of the original error, the fix is confirmed.
|
|
414
414
|
|
|
415
|
-
Q: What
|
|
416
|
-
A: false (default): Sentinel opens a GitHub PR for admin review; merge it when satisfied.
|
|
417
|
-
true: Sentinel pushes directly to main
|
|
415
|
+
Q: What are AUTO_COMMIT and AUTO_RELEASE?
|
|
416
|
+
A: AUTO_COMMIT=false (default): Sentinel opens a GitHub PR for admin review; merge it when satisfied.
|
|
417
|
+
AUTO_COMMIT=true: Sentinel pushes directly to main automatically.
|
|
418
|
+
AUTO_RELEASE=false (default): no CI/CD pipeline triggered; admin releases manually.
|
|
419
|
+
AUTO_RELEASE=true: Sentinel triggers Jenkins/GHA after each push to main.
|
|
418
420
|
|
|
419
421
|
Q: How do I pause Sentinel without stopping the process?
|
|
420
422
|
A: "pause sentinel" — creates a SENTINEL_PAUSE file. All auto-fix activity stops
|
|
@@ -445,8 +447,8 @@ SENTINEL ARCHITECTURE
|
|
|
445
447
|
- Fix engine: Claude Code headless (claude --print) with structured prompt (error + stack trace
|
|
446
448
|
+ Cairn MCP context); unified diff output; max 5 files / 200 lines
|
|
447
449
|
- Commit: git pull --rebase, apply patch, run tests, commit with sentinel/fix-<fp> marker
|
|
448
|
-
- Publish:
|
|
449
|
-
|
|
450
|
+
- Publish: AUTO_COMMIT=true → push to main; AUTO_COMMIT=false → branch + GitHub PR
|
|
451
|
+
AUTO_RELEASE=true → CI/CD trigger after push; AUTO_RELEASE=false → manual release
|
|
450
452
|
- Fix confirmation: SENTINEL marker injected into modified methods; marker appearing in
|
|
451
453
|
production logs starts quiet period; after MARKER_CONFIRM_HOURS with no recurrence → confirmed
|
|
452
454
|
|
|
@@ -474,7 +476,8 @@ Release management:
|
|
|
474
476
|
Key config options:
|
|
475
477
|
- ANTHROPIC_API_KEY: Boss conversation (structured tool-use); optional if CLAUDE_PRO_FOR_TASKS=true
|
|
476
478
|
- CLAUDE_PRO_FOR_TASKS=true (default): Fix Engine uses claude CLI (Claude Pro OAuth billing)
|
|
477
|
-
-
|
|
479
|
+
- AUTO_COMMIT=false (default): Sentinel opens PRs; =true: pushes directly to main
|
|
480
|
+
- AUTO_RELEASE=false (default): no CI/CD trigger; =true: triggers Jenkins/GHA after push
|
|
478
481
|
- SYNC_RETENTION_DAYS (default 30): delete synced logs older than N days
|
|
479
482
|
- SYNC_MAX_FILE_MB (default 200): truncate synced logs exceeding this size
|
|
480
483
|
- HEALTH_URL: HTTP endpoint per repo; JSON with "Status": "true" = healthy
|
|
@@ -713,9 +716,11 @@ _TOOLS = [
|
|
|
713
716
|
"name": "get_status",
|
|
714
717
|
"description": (
|
|
715
718
|
"Get recent errors, fixes applied, fixes pending review, open PRs, "
|
|
719
|
+
"pending releases (commits pushed to main but not yet released — AUTO_RELEASE=false), "
|
|
716
720
|
"and the live task queue (issues and repo tasks waiting to be picked up). "
|
|
717
721
|
"Use for: 'what happened today?', 'any issues?', 'how are things?', "
|
|
718
722
|
"'what are the open PRs?', 'did sentinel fix anything?', "
|
|
723
|
+
"'what needs to be released?', 'which repos have unreleased commits?', "
|
|
719
724
|
"'what is queued?', 'what is pending?', 'progress on X?'"
|
|
720
725
|
),
|
|
721
726
|
"input_schema": {
|
|
@@ -873,7 +878,7 @@ _TOOLS = [
|
|
|
873
878
|
"description": (
|
|
874
879
|
"ADMIN ONLY. Submit a feature, fix, refactor, or scheduled deploy task for a managed repo. "
|
|
875
880
|
"Claude Code will run against the repo's local clone, implement the change, "
|
|
876
|
-
"commit, and push (or open a PR if
|
|
881
|
+
"commit, and push (or open a PR if AUTO_COMMIT=false). "
|
|
877
882
|
"ALWAYS gather a complete spec first — ask follow-up questions until unambiguous. "
|
|
878
883
|
"Use for: 'add X to elprint-connector-service', 'fix Y in cairn', "
|
|
879
884
|
"'refactor OrderService', 'deploy UIB at 3 AM tomorrow', "
|
|
@@ -1517,7 +1522,7 @@ _TOOLS = [
|
|
|
1517
1522
|
"ADMIN ONLY. Merge a branch or PR into the main branch. "
|
|
1518
1523
|
"ALWAYS call with confirmed=false first to show details for admin review — "
|
|
1519
1524
|
"never merge without showing the plan first. "
|
|
1520
|
-
"Use for Sentinel fix PRs (
|
|
1525
|
+
"Use for Sentinel fix PRs (AUTO_COMMIT=false), Renovate/external PRs by number, "
|
|
1521
1526
|
"or any arbitrary branch by name (branch_name). "
|
|
1522
1527
|
"confirmed=false fetches and shows details; confirmed=true executes the merge. "
|
|
1523
1528
|
"e.g. 'merge the fix for Whydah-TypeLib', 'merge TypeLib PR #247', "
|
|
@@ -1618,7 +1623,7 @@ _TOOLS = [
|
|
|
1618
1623
|
"enum": ["build", "release", "update_deps", "release_and_cascade"],
|
|
1619
1624
|
"description": (
|
|
1620
1625
|
"build: trigger Jenkins build only. "
|
|
1621
|
-
"release: trigger Maven Release; auto-cascades if
|
|
1626
|
+
"release: trigger Maven Release; auto-cascades if AUTO_COMMIT=true. "
|
|
1622
1627
|
"update_deps: update dependency version in target repos — source is ALREADY released, do NOT trigger a new release. "
|
|
1623
1628
|
"Use this when the user says 'update X to version Y' or 'bump X dep to Y in repo Z'. "
|
|
1624
1629
|
"release_and_cascade: release source_repo then cascade to all dependents."
|
|
@@ -2029,6 +2034,7 @@ async def _run_tool(name: str, inputs: dict, cfg_loader, store, slack_client=Non
|
|
|
2029
2034
|
"queued_issues": queued_issues,
|
|
2030
2035
|
"queued_repo_tasks": pending_repo_tasks,
|
|
2031
2036
|
"scheduled_tasks": scheduled_tasks,
|
|
2037
|
+
"pending_releases": store.get_pending_releases(),
|
|
2032
2038
|
})
|
|
2033
2039
|
|
|
2034
2040
|
if name == "check_auth_status":
|
|
@@ -4312,7 +4318,7 @@ async def _run_tool(name: str, inputs: dict, cfg_loader, store, slack_client=Non
|
|
|
4312
4318
|
"job_url": repo.cicd_job_url,
|
|
4313
4319
|
"cascade": cascade_plan["dependents"],
|
|
4314
4320
|
"cascade_note": cascade_note,
|
|
4315
|
-
"
|
|
4321
|
+
"auto_commit_source": repo.auto_commit,
|
|
4316
4322
|
"confirm_prompt": "Reply with confirmed=true to proceed.",
|
|
4317
4323
|
})
|
|
4318
4324
|
|
|
@@ -4345,9 +4351,11 @@ async def _run_tool(name: str, inputs: dict, cfg_loader, store, slack_client=Non
|
|
|
4345
4351
|
logger.info("Boss manage_release: release triggered for %s by %s (success=%s)", source_repo, user_id, success)
|
|
4346
4352
|
if not success:
|
|
4347
4353
|
return json.dumps({"status": "failed", "repo": source_repo, "error": "Jenkins release trigger failed — check logs"})
|
|
4354
|
+
store.clear_pending_releases(source_repo)
|
|
4348
4355
|
|
|
4349
|
-
# Cascade immediately if release_and_cascade, or if
|
|
4350
|
-
|
|
4356
|
+
# Cascade immediately if release_and_cascade, or if AUTO_COMMIT=true
|
|
4357
|
+
from .config_loader import resolve_auto_commit as _resolve_auto_commit
|
|
4358
|
+
do_cascade = (operation == "release_and_cascade") or _resolve_auto_commit(repo, cfg_loader.sentinel)
|
|
4351
4359
|
if do_cascade:
|
|
4352
4360
|
artifact_id = get_artifact_id(repo.local_path)
|
|
4353
4361
|
new_version = get_release_version(repo.local_path)
|
|
@@ -4365,7 +4373,7 @@ async def _run_tool(name: str, inputs: dict, cfg_loader, store, slack_client=Non
|
|
|
4365
4373
|
"version": new_version,
|
|
4366
4374
|
"cascade": [{"repo": r.repo_name, "success": r.success, "pr_url": r.pr_url, "error": r.error} for r in results],
|
|
4367
4375
|
})
|
|
4368
|
-
return json.dumps({"status": "released", "repo": source_repo, "note": "Cascade will run automatically on next poll if
|
|
4376
|
+
return json.dumps({"status": "released", "repo": source_repo, "note": "Cascade will run automatically on next poll if AUTO_COMMIT=true."})
|
|
4369
4377
|
|
|
4370
4378
|
if operation == "update_deps":
|
|
4371
4379
|
from .dependency_manager import get_latest_released_version
|
|
@@ -4427,10 +4435,11 @@ async def _run_tool(name: str, inputs: dict, cfg_loader, store, slack_client=Non
|
|
|
4427
4435
|
"artifact_id": artifact_id,
|
|
4428
4436
|
"release_version": release_ver,
|
|
4429
4437
|
"cicd_url": repo.cicd_job_url,
|
|
4430
|
-
"
|
|
4438
|
+
"auto_commit": repo.auto_commit,
|
|
4431
4439
|
})
|
|
4432
4440
|
|
|
4433
4441
|
# ── Plan phase ───────────────────────────────────────────────────────
|
|
4442
|
+
from .config_loader import resolve_auto_commit as _resolve_auto_commit
|
|
4434
4443
|
if not confirmed:
|
|
4435
4444
|
plan_steps = []
|
|
4436
4445
|
for i, step in enumerate(steps):
|
|
@@ -4448,7 +4457,7 @@ async def _run_tool(name: str, inputs: dict, cfg_loader, store, slack_client=Non
|
|
|
4448
4457
|
"repo": step["repo"],
|
|
4449
4458
|
"release_version": step["release_version"],
|
|
4450
4459
|
"jenkins_url": step["cicd_url"],
|
|
4451
|
-
"mode": "push to main" if step["
|
|
4460
|
+
"mode": "push to main" if step["auto_commit"] else "open PR",
|
|
4452
4461
|
})
|
|
4453
4462
|
return json.dumps({
|
|
4454
4463
|
"plan": f"Sequential release chain: {' → '.join(s['repo'] for s in steps)}",
|
|
@@ -77,6 +77,16 @@ class StateStore:
|
|
|
77
77
|
dismissed_at TEXT NOT NULL,
|
|
78
78
|
note TEXT -- optional free-text explanation
|
|
79
79
|
);
|
|
80
|
+
|
|
81
|
+
CREATE TABLE IF NOT EXISTS pending_releases (
|
|
82
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
83
|
+
repo_name TEXT NOT NULL,
|
|
84
|
+
commit_hash TEXT NOT NULL,
|
|
85
|
+
branch TEXT NOT NULL,
|
|
86
|
+
description TEXT, -- short summary of what was committed
|
|
87
|
+
fingerprint TEXT, -- error/issue fingerprint that triggered it
|
|
88
|
+
committed_at TEXT NOT NULL
|
|
89
|
+
);
|
|
80
90
|
""")
|
|
81
91
|
self._migrate()
|
|
82
92
|
logger.debug("StateStore initialised at %s", self.db_path)
|
|
@@ -629,6 +639,33 @@ class StateStore:
|
|
|
629
639
|
).fetchall()
|
|
630
640
|
return [dict(r) for r in rows]
|
|
631
641
|
|
|
642
|
+
# ── Pending releases ──────────────────────────────────────────────────────
|
|
643
|
+
|
|
644
|
+
def add_pending_release(self, repo_name: str, commit_hash: str, branch: str,
|
|
645
|
+
description: str = "", fingerprint: str = "") -> None:
|
|
646
|
+
"""Record that a commit was pushed to main but not yet released (AUTO_RELEASE=false)."""
|
|
647
|
+
with self._conn() as conn:
|
|
648
|
+
conn.execute(
|
|
649
|
+
"INSERT INTO pending_releases (repo_name, commit_hash, branch, description, fingerprint, committed_at) "
|
|
650
|
+
"VALUES (?, ?, ?, ?, ?, ?)",
|
|
651
|
+
(repo_name, commit_hash, branch, description, fingerprint, _now()),
|
|
652
|
+
)
|
|
653
|
+
|
|
654
|
+
def clear_pending_releases(self, repo_name: str) -> int:
|
|
655
|
+
"""Mark all pending releases for a repo as cleared (after a release is triggered). Returns rows deleted."""
|
|
656
|
+
with self._conn() as conn:
|
|
657
|
+
cur = conn.execute("DELETE FROM pending_releases WHERE repo_name=?", (repo_name,))
|
|
658
|
+
return cur.rowcount
|
|
659
|
+
|
|
660
|
+
def get_pending_releases(self) -> list[dict]:
|
|
661
|
+
"""Return all repos with commits pushed but not yet released."""
|
|
662
|
+
with self._conn() as conn:
|
|
663
|
+
rows = conn.execute(
|
|
664
|
+
"SELECT repo_name, commit_hash, branch, description, fingerprint, committed_at "
|
|
665
|
+
"FROM pending_releases ORDER BY committed_at ASC"
|
|
666
|
+
).fetchall()
|
|
667
|
+
return [dict(r) for r in rows]
|
|
668
|
+
|
|
632
669
|
# ── Knowledge cache (ask_codebase results) ────────────────────────────────
|
|
633
670
|
|
|
634
671
|
def get_knowledge(self, repo_name: str, question: str) -> "str | None":
|