aidp 0.1.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +59 -4
- data/bin/aidp +2 -2
- data/lib/aidp/analyze/agent_personas.rb +1 -1
- data/lib/aidp/analyze/data_retention_manager.rb +2 -2
- data/lib/aidp/analyze/database.rb +99 -82
- data/lib/aidp/analyze/error_handler.rb +12 -76
- data/lib/aidp/analyze/focus_guidance.rb +2 -2
- data/lib/aidp/analyze/large_analysis_progress.rb +2 -2
- data/lib/aidp/analyze/metrics_storage.rb +336 -0
- data/lib/aidp/analyze/prioritizer.rb +4 -4
- data/lib/aidp/analyze/repository_chunker.rb +15 -13
- data/lib/aidp/analyze/ruby_maat_integration.rb +6 -102
- data/lib/aidp/analyze/runner.rb +107 -191
- data/lib/aidp/analyze/steps.rb +29 -30
- data/lib/aidp/analyze/storage.rb +234 -172
- data/lib/aidp/cli/jobs_command.rb +489 -0
- data/lib/aidp/cli/terminal_io.rb +52 -0
- data/lib/aidp/cli.rb +227 -0
- data/lib/aidp/config.rb +33 -0
- data/lib/aidp/core_ext/class_attribute.rb +36 -0
- data/lib/aidp/database/pg_adapter.rb +148 -0
- data/lib/aidp/database_config.rb +69 -0
- data/lib/aidp/database_connection.rb +72 -0
- data/lib/aidp/database_migration.rb +158 -0
- data/lib/aidp/execute/runner.rb +65 -92
- data/lib/aidp/execute/steps.rb +81 -82
- data/lib/aidp/job_manager.rb +41 -0
- data/lib/aidp/jobs/base_job.rb +47 -0
- data/lib/aidp/jobs/provider_execution_job.rb +96 -0
- data/lib/aidp/project_detector.rb +117 -0
- data/lib/aidp/provider_manager.rb +25 -0
- data/lib/aidp/providers/agent_supervisor.rb +348 -0
- data/lib/aidp/providers/anthropic.rb +187 -0
- data/lib/aidp/providers/base.rb +162 -0
- data/lib/aidp/providers/cursor.rb +304 -0
- data/lib/aidp/providers/gemini.rb +187 -0
- data/lib/aidp/providers/macos_ui.rb +24 -0
- data/lib/aidp/providers/supervised_base.rb +317 -0
- data/lib/aidp/providers/supervised_cursor.rb +22 -0
- data/lib/aidp/sync.rb +13 -0
- data/lib/aidp/util.rb +39 -0
- data/lib/aidp/{shared/version.rb → version.rb} +1 -3
- data/lib/aidp/workspace.rb +19 -0
- data/lib/aidp.rb +36 -45
- data/templates/ANALYZE/01_REPOSITORY_ANALYSIS.md +4 -4
- metadata +89 -45
- data/lib/aidp/shared/cli.rb +0 -117
- data/lib/aidp/shared/config.rb +0 -35
- data/lib/aidp/shared/project_detector.rb +0 -119
- data/lib/aidp/shared/providers/anthropic.rb +0 -26
- data/lib/aidp/shared/providers/base.rb +0 -17
- data/lib/aidp/shared/providers/cursor.rb +0 -102
- data/lib/aidp/shared/providers/gemini.rb +0 -26
- data/lib/aidp/shared/providers/macos_ui.rb +0 -26
- data/lib/aidp/shared/sync.rb +0 -15
- data/lib/aidp/shared/util.rb +0 -41
- data/lib/aidp/shared/workspace.rb +0 -21
@@ -0,0 +1,158 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "sqlite3"
|
4
|
+
require "fileutils"
|
5
|
+
|
6
|
+
module Aidp
|
7
|
+
# Handles database migrations for AIDP
|
8
|
+
class DatabaseMigration
|
9
|
+
def initialize(project_dir = Dir.pwd)
|
10
|
+
@project_dir = project_dir
|
11
|
+
@old_db_path = File.join(project_dir, ".aidp-analysis.db")
|
12
|
+
@new_db_path = File.join(project_dir, ".aidp.db")
|
13
|
+
end
|
14
|
+
|
15
|
+
# Migrate database from old to new format
|
16
|
+
def migrate
|
17
|
+
# If neither database exists, create new one directly
|
18
|
+
if !File.exist?(@old_db_path) && !File.exist?(@new_db_path)
|
19
|
+
create_new_database
|
20
|
+
return true
|
21
|
+
end
|
22
|
+
|
23
|
+
# If new database already exists, skip migration
|
24
|
+
if File.exist?(@new_db_path)
|
25
|
+
puts "Database .aidp.db already exists, skipping migration"
|
26
|
+
return false
|
27
|
+
end
|
28
|
+
|
29
|
+
# Rename old database to new name
|
30
|
+
FileUtils.mv(@old_db_path, @new_db_path)
|
31
|
+
|
32
|
+
# Open database connection
|
33
|
+
db = SQLite3::Database.new(@new_db_path)
|
34
|
+
|
35
|
+
# Create new tables for job management
|
36
|
+
create_job_tables(db)
|
37
|
+
|
38
|
+
# Close connection
|
39
|
+
db.close
|
40
|
+
|
41
|
+
true
|
42
|
+
rescue => e
|
43
|
+
puts "Error during database migration: #{e.message}"
|
44
|
+
# Try to restore old database if something went wrong
|
45
|
+
if File.exist?(@new_db_path) && !File.exist?(@old_db_path)
|
46
|
+
FileUtils.mv(@new_db_path, @old_db_path)
|
47
|
+
end
|
48
|
+
false
|
49
|
+
end
|
50
|
+
|
51
|
+
private
|
52
|
+
|
53
|
+
def create_new_database
|
54
|
+
db = SQLite3::Database.new(@new_db_path)
|
55
|
+
|
56
|
+
# Create original tables
|
57
|
+
create_original_tables(db)
|
58
|
+
|
59
|
+
# Create new job tables
|
60
|
+
create_job_tables(db)
|
61
|
+
|
62
|
+
db.close
|
63
|
+
end
|
64
|
+
|
65
|
+
def create_original_tables(db)
|
66
|
+
# Create analysis_results table
|
67
|
+
db.execute(<<~SQL)
|
68
|
+
CREATE TABLE analysis_results (
|
69
|
+
step_name TEXT PRIMARY KEY,
|
70
|
+
data TEXT NOT NULL,
|
71
|
+
metadata TEXT,
|
72
|
+
created_at TEXT NOT NULL,
|
73
|
+
updated_at TEXT NOT NULL
|
74
|
+
)
|
75
|
+
SQL
|
76
|
+
|
77
|
+
# Create analysis_metrics table
|
78
|
+
db.execute(<<~SQL)
|
79
|
+
CREATE TABLE analysis_metrics (
|
80
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
81
|
+
step_name TEXT NOT NULL,
|
82
|
+
metric_name TEXT NOT NULL,
|
83
|
+
value TEXT NOT NULL,
|
84
|
+
recorded_at TEXT NOT NULL,
|
85
|
+
UNIQUE(step_name, metric_name, recorded_at)
|
86
|
+
)
|
87
|
+
SQL
|
88
|
+
|
89
|
+
# Create embeddings table
|
90
|
+
db.execute(<<~SQL)
|
91
|
+
CREATE TABLE embeddings (
|
92
|
+
step_name TEXT PRIMARY KEY,
|
93
|
+
embeddings_data TEXT NOT NULL,
|
94
|
+
created_at TEXT NOT NULL
|
95
|
+
)
|
96
|
+
SQL
|
97
|
+
|
98
|
+
# Create indexes
|
99
|
+
db.execute("CREATE INDEX idx_analysis_metrics_step_name ON analysis_metrics(step_name)")
|
100
|
+
db.execute("CREATE INDEX idx_analysis_metrics_recorded_at ON analysis_metrics(recorded_at)")
|
101
|
+
db.execute("CREATE INDEX idx_analysis_results_updated_at ON analysis_results(updated_at)")
|
102
|
+
end
|
103
|
+
|
104
|
+
def create_job_tables(db)
|
105
|
+
# Create jobs table
|
106
|
+
db.execute(<<~SQL)
|
107
|
+
CREATE TABLE jobs (
|
108
|
+
id INTEGER PRIMARY KEY,
|
109
|
+
job_type TEXT NOT NULL,
|
110
|
+
provider TEXT NOT NULL,
|
111
|
+
status TEXT NOT NULL,
|
112
|
+
created_at INTEGER NOT NULL,
|
113
|
+
started_at INTEGER,
|
114
|
+
completed_at INTEGER,
|
115
|
+
error TEXT,
|
116
|
+
metadata TEXT
|
117
|
+
)
|
118
|
+
SQL
|
119
|
+
|
120
|
+
# Create job_executions table
|
121
|
+
db.execute(<<~SQL)
|
122
|
+
CREATE TABLE job_executions (
|
123
|
+
id INTEGER PRIMARY KEY,
|
124
|
+
job_id INTEGER NOT NULL,
|
125
|
+
attempt INTEGER NOT NULL,
|
126
|
+
status TEXT NOT NULL,
|
127
|
+
started_at INTEGER NOT NULL,
|
128
|
+
completed_at INTEGER,
|
129
|
+
error TEXT,
|
130
|
+
FOREIGN KEY (job_id) REFERENCES jobs(id)
|
131
|
+
)
|
132
|
+
SQL
|
133
|
+
|
134
|
+
# Create job_logs table
|
135
|
+
db.execute(<<~SQL)
|
136
|
+
CREATE TABLE job_logs (
|
137
|
+
id INTEGER PRIMARY KEY,
|
138
|
+
job_id INTEGER NOT NULL,
|
139
|
+
execution_id INTEGER NOT NULL,
|
140
|
+
timestamp INTEGER NOT NULL,
|
141
|
+
message TEXT NOT NULL,
|
142
|
+
level TEXT NOT NULL,
|
143
|
+
metadata TEXT,
|
144
|
+
FOREIGN KEY (job_id) REFERENCES jobs(id),
|
145
|
+
FOREIGN KEY (execution_id) REFERENCES job_executions(id)
|
146
|
+
)
|
147
|
+
SQL
|
148
|
+
|
149
|
+
# Create indexes for job tables
|
150
|
+
db.execute("CREATE INDEX idx_jobs_status ON jobs(status)")
|
151
|
+
db.execute("CREATE INDEX idx_jobs_provider ON jobs(provider)")
|
152
|
+
db.execute("CREATE INDEX idx_job_executions_job_id ON job_executions(job_id)")
|
153
|
+
db.execute("CREATE INDEX idx_job_logs_job_id ON job_logs(job_id)")
|
154
|
+
db.execute("CREATE INDEX idx_job_logs_execution_id ON job_logs(execution_id)")
|
155
|
+
db.execute("CREATE INDEX idx_job_logs_timestamp ON job_logs(timestamp)")
|
156
|
+
end
|
157
|
+
end
|
158
|
+
end
|
data/lib/aidp/execute/runner.rb
CHANGED
@@ -1,135 +1,108 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "erb"
|
4
|
-
require "yaml"
|
5
|
-
require "json"
|
6
|
-
|
7
3
|
module Aidp
|
8
4
|
module Execute
|
9
|
-
# Handles execution logic for execute mode steps
|
10
5
|
class Runner
|
11
|
-
attr_reader :project_dir, :progress
|
12
|
-
|
13
6
|
def initialize(project_dir)
|
14
7
|
@project_dir = project_dir
|
15
|
-
@progress = Aidp::Execute::Progress.new(project_dir)
|
16
8
|
end
|
17
9
|
|
18
|
-
def
|
19
|
-
|
10
|
+
def progress
|
11
|
+
@progress ||= Aidp::Execute::Progress.new(@project_dir)
|
12
|
+
end
|
20
13
|
|
14
|
+
def run_step(step_name, options = {})
|
15
|
+
# Always validate step exists first, even in mock mode
|
21
16
|
step_spec = Aidp::Execute::Steps::SPEC[step_name]
|
22
|
-
|
23
|
-
|
24
|
-
# Load template
|
25
|
-
template = find_template(template_name)
|
26
|
-
raise "Template '#{template_name}' not found" unless template
|
17
|
+
raise "Step '#{step_name}' not found" unless step_spec
|
27
18
|
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
if options[:simulate_error]
|
33
|
-
return {
|
34
|
-
status: "error",
|
35
|
-
error: options[:simulate_error],
|
36
|
-
step: step_name
|
37
|
-
}
|
19
|
+
if should_use_mock_mode?(options)
|
20
|
+
return options[:simulate_error] ?
|
21
|
+
{status: "error", error: options[:simulate_error]} :
|
22
|
+
mock_execution_result
|
38
23
|
end
|
39
24
|
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
25
|
+
job = Aidp::Jobs::ProviderExecutionJob.enqueue(
|
26
|
+
provider_type: "cursor",
|
27
|
+
prompt: composed_prompt(step_name, options),
|
28
|
+
metadata: {
|
29
|
+
step_name: step_name,
|
30
|
+
project_dir: @project_dir
|
31
|
+
}
|
32
|
+
)
|
47
33
|
|
48
|
-
|
49
|
-
|
34
|
+
wait_for_job_completion(job)
|
35
|
+
end
|
50
36
|
|
51
|
-
|
52
|
-
generate_output_files(step_name, step_spec["outs"], result)
|
37
|
+
private
|
53
38
|
|
54
|
-
|
55
|
-
|
39
|
+
def should_use_mock_mode?(options)
|
40
|
+
options[:mock_mode] || ENV["AIDP_MOCK_MODE"] == "1" || ENV["RAILS_ENV"] == "test"
|
41
|
+
end
|
56
42
|
|
57
|
-
|
43
|
+
def mock_execution_result
|
44
|
+
{
|
45
|
+
status: "completed",
|
46
|
+
provider: "mock",
|
47
|
+
message: "Mock execution",
|
48
|
+
output: "Mock execution result"
|
49
|
+
}
|
58
50
|
end
|
59
51
|
|
60
|
-
|
52
|
+
def wait_for_job_completion(job_id)
|
53
|
+
loop do
|
54
|
+
job = Que.execute("SELECT * FROM que_jobs WHERE id = $1", [job_id]).first
|
55
|
+
return {status: "completed"} if job && job["finished_at"] && job["error_count"] == 0
|
56
|
+
return {status: "failed", error: job["last_error_message"]} if job && job["error_count"] && job["error_count"] > 0
|
57
|
+
|
58
|
+
if job && job["finished_at"].nil?
|
59
|
+
duration = Time.now - job["run_at"]
|
60
|
+
minutes = (duration / 60).to_i
|
61
|
+
seconds = (duration % 60).to_i
|
62
|
+
duration_str = (minutes > 0) ? "#{minutes}m #{seconds}s" : "#{seconds}s"
|
63
|
+
print "\r🔄 Job #{job_id} is running (#{duration_str})...".ljust(80)
|
64
|
+
else
|
65
|
+
print "\r⏳ Job #{job_id} is pending...".ljust(80)
|
66
|
+
end
|
67
|
+
$stdout.flush
|
68
|
+
sleep 1
|
69
|
+
end
|
70
|
+
ensure
|
71
|
+
print "\r" + " " * 80 + "\r"
|
72
|
+
end
|
61
73
|
|
62
74
|
def find_template(template_name)
|
63
75
|
template_search_paths.each do |path|
|
64
|
-
|
65
|
-
return
|
76
|
+
template_path = File.join(path, template_name)
|
77
|
+
return template_path if File.exist?(template_path)
|
66
78
|
end
|
67
79
|
nil
|
68
80
|
end
|
69
81
|
|
70
82
|
def template_search_paths
|
71
83
|
[
|
72
|
-
File.join(@project_dir, "templates"),
|
73
|
-
File.join(@project_dir, "templates", "COMMON")
|
74
|
-
File.join(File.dirname(__FILE__), "..", "..", "..", "templates", "EXECUTE"),
|
75
|
-
File.join(File.dirname(__FILE__), "..", "..", "..", "templates", "COMMON")
|
84
|
+
File.join(@project_dir, "templates", "EXECUTE"),
|
85
|
+
File.join(@project_dir, "templates", "COMMON")
|
76
86
|
]
|
77
87
|
end
|
78
88
|
|
79
|
-
def composed_prompt(
|
80
|
-
|
81
|
-
|
89
|
+
def composed_prompt(step_name, options = {})
|
90
|
+
step_spec = Aidp::Execute::Steps::SPEC[step_name]
|
91
|
+
raise "Step '#{step_name}' not found" unless step_spec
|
92
|
+
|
93
|
+
template_name = step_spec["templates"].first
|
94
|
+
template_path = find_template(template_name)
|
95
|
+
raise "Template not found for step #{step_name}" unless template_path
|
82
96
|
|
83
|
-
|
84
|
-
agent_base = find_template("AGENT_BASE.md")
|
85
|
-
template = "#{agent_base}\n\n#{template}" if agent_base
|
97
|
+
template = File.read(template_path)
|
86
98
|
|
87
|
-
# Replace
|
99
|
+
# Replace template variables in the format {{key}} with option values
|
88
100
|
options.each do |key, value|
|
89
101
|
template = template.gsub("{{#{key}}}", value.to_s)
|
90
102
|
end
|
91
103
|
|
92
104
|
template
|
93
105
|
end
|
94
|
-
|
95
|
-
def generate_output_files(step_name, output_files, result)
|
96
|
-
output_files.each do |output_file|
|
97
|
-
file_path = File.join(@project_dir, output_file)
|
98
|
-
content = generate_output_content(step_name, output_file, result)
|
99
|
-
File.write(file_path, content)
|
100
|
-
end
|
101
|
-
end
|
102
|
-
|
103
|
-
def generate_output_content(step_name, output_file, result)
|
104
|
-
case output_file
|
105
|
-
when /\.md$/
|
106
|
-
"# #{step_name} Output\n\nGenerated on #{Time.now}\n\n## Result\n\n#{result[:status]}"
|
107
|
-
when /\.json$/
|
108
|
-
result.to_json
|
109
|
-
else
|
110
|
-
"Output for #{step_name}: #{result[:status]}"
|
111
|
-
end
|
112
|
-
end
|
113
|
-
|
114
|
-
def generate_database_export
|
115
|
-
database_file = File.join(@project_dir, ".aidp.db")
|
116
|
-
require "sqlite3"
|
117
|
-
|
118
|
-
begin
|
119
|
-
db = SQLite3::Database.new(database_file)
|
120
|
-
db.execute("CREATE TABLE IF NOT EXISTS execute_results (step TEXT, status TEXT, completed_at TEXT)")
|
121
|
-
|
122
|
-
Aidp::Execute::Steps::SPEC.keys.each do |step|
|
123
|
-
if @progress.step_completed?(step)
|
124
|
-
db.execute("INSERT INTO execute_results (step, status, completed_at) VALUES (?, ?, ?)",
|
125
|
-
[step, "success", Time.now.iso8601])
|
126
|
-
end
|
127
|
-
end
|
128
|
-
rescue => e
|
129
|
-
# Log the error but don't fail the execution
|
130
|
-
puts "Warning: Database export failed: #{e.message}"
|
131
|
-
end
|
132
|
-
end
|
133
106
|
end
|
134
107
|
end
|
135
108
|
end
|
data/lib/aidp/execute/steps.rb
CHANGED
@@ -2,110 +2,109 @@
|
|
2
2
|
|
3
3
|
module Aidp
|
4
4
|
module Execute
|
5
|
-
|
6
|
-
class Steps
|
5
|
+
module Steps
|
7
6
|
SPEC = {
|
8
7
|
"00_PRD" => {
|
9
|
-
"templates" => ["
|
10
|
-
"
|
11
|
-
"
|
12
|
-
"
|
8
|
+
"templates" => ["prd.md"],
|
9
|
+
"description" => "Generate Product Requirements Document",
|
10
|
+
"outs" => ["docs/prd.md"],
|
11
|
+
"gate" => true
|
13
12
|
},
|
14
13
|
"01_NFRS" => {
|
15
|
-
"templates" => ["
|
16
|
-
"
|
17
|
-
"
|
18
|
-
"
|
14
|
+
"templates" => ["nfrs.md"],
|
15
|
+
"description" => "Define Non-Functional Requirements",
|
16
|
+
"outs" => ["docs/nfrs.md"],
|
17
|
+
"gate" => true
|
19
18
|
},
|
20
19
|
"02_ARCHITECTURE" => {
|
21
|
-
"templates" => ["
|
22
|
-
"
|
23
|
-
"
|
24
|
-
"
|
20
|
+
"templates" => ["architecture.md"],
|
21
|
+
"description" => "Design System Architecture",
|
22
|
+
"outs" => ["docs/architecture.md"],
|
23
|
+
"gate" => true
|
25
24
|
},
|
26
25
|
"02A_ARCH_GATE_QUESTIONS" => {
|
27
|
-
"templates" => ["
|
28
|
-
"
|
29
|
-
"
|
30
|
-
"
|
26
|
+
"templates" => ["arch_gate_questions.md"],
|
27
|
+
"description" => "Architecture Gate Questions",
|
28
|
+
"outs" => ["docs/arch_gate_questions.md"],
|
29
|
+
"gate" => true
|
31
30
|
},
|
32
31
|
"03_ADR_FACTORY" => {
|
33
|
-
"templates" => ["
|
34
|
-
"
|
35
|
-
"
|
36
|
-
"
|
32
|
+
"templates" => ["adr_factory.md"],
|
33
|
+
"description" => "Generate Architecture Decision Records",
|
34
|
+
"outs" => ["docs/adr/*.md"],
|
35
|
+
"gate" => false
|
37
36
|
},
|
38
37
|
"04_DOMAIN_DECOMPOSITION" => {
|
39
|
-
"templates" => ["
|
40
|
-
"
|
41
|
-
"
|
42
|
-
"
|
43
|
-
},
|
44
|
-
"
|
45
|
-
"templates" => ["
|
46
|
-
"
|
47
|
-
"
|
48
|
-
"
|
49
|
-
},
|
50
|
-
"
|
51
|
-
"templates" => ["
|
52
|
-
"
|
53
|
-
"
|
54
|
-
"
|
55
|
-
},
|
56
|
-
"
|
57
|
-
"templates" => ["
|
58
|
-
"
|
59
|
-
"
|
60
|
-
"
|
61
|
-
},
|
62
|
-
"
|
63
|
-
"templates" => ["
|
64
|
-
"
|
65
|
-
"
|
66
|
-
"
|
67
|
-
},
|
68
|
-
"
|
69
|
-
"templates" => ["
|
70
|
-
"
|
71
|
-
"
|
72
|
-
"
|
73
|
-
},
|
74
|
-
"
|
75
|
-
"templates" => ["
|
76
|
-
"
|
77
|
-
"
|
78
|
-
"
|
38
|
+
"templates" => ["domain_decomposition.md"],
|
39
|
+
"description" => "Decompose Domain into Components",
|
40
|
+
"outs" => ["docs/domain_decomposition.md"],
|
41
|
+
"gate" => true
|
42
|
+
},
|
43
|
+
"05_API_DESIGN" => {
|
44
|
+
"templates" => ["api_design.md"],
|
45
|
+
"description" => "Design APIs and Interfaces",
|
46
|
+
"outs" => ["docs/api_design.md"],
|
47
|
+
"gate" => true
|
48
|
+
},
|
49
|
+
"06_DATA_MODEL" => {
|
50
|
+
"templates" => ["data_model.md"],
|
51
|
+
"description" => "Design Data Model",
|
52
|
+
"outs" => ["docs/data_model.md"],
|
53
|
+
"gate" => true
|
54
|
+
},
|
55
|
+
"07_SECURITY_REVIEW" => {
|
56
|
+
"templates" => ["security_review.md"],
|
57
|
+
"description" => "Security Review and Threat Model",
|
58
|
+
"outs" => ["docs/security_review.md"],
|
59
|
+
"gate" => true
|
60
|
+
},
|
61
|
+
"08_PERFORMANCE_REVIEW" => {
|
62
|
+
"templates" => ["performance_review.md"],
|
63
|
+
"description" => "Performance Review and Optimization",
|
64
|
+
"outs" => ["docs/performance_review.md"],
|
65
|
+
"gate" => true
|
66
|
+
},
|
67
|
+
"09_RELIABILITY_REVIEW" => {
|
68
|
+
"templates" => ["reliability_review.md"],
|
69
|
+
"description" => "Reliability Review and SLOs",
|
70
|
+
"outs" => ["docs/reliability_review.md"],
|
71
|
+
"gate" => true
|
72
|
+
},
|
73
|
+
"10_TESTING_STRATEGY" => {
|
74
|
+
"templates" => ["testing_strategy.md"],
|
75
|
+
"description" => "Define Testing Strategy",
|
76
|
+
"outs" => ["docs/testing_strategy.md"],
|
77
|
+
"gate" => true
|
79
78
|
},
|
80
79
|
"11_STATIC_ANALYSIS" => {
|
81
|
-
"templates" => ["
|
82
|
-
"
|
83
|
-
"
|
84
|
-
"
|
80
|
+
"templates" => ["static_analysis.md"],
|
81
|
+
"description" => "Static Code Analysis",
|
82
|
+
"outs" => ["docs/static_analysis.md"],
|
83
|
+
"gate" => false
|
85
84
|
},
|
86
85
|
"12_OBSERVABILITY_SLOS" => {
|
87
|
-
"templates" => ["
|
88
|
-
"
|
89
|
-
"
|
90
|
-
"
|
86
|
+
"templates" => ["observability_slos.md"],
|
87
|
+
"description" => "Define Observability and SLOs",
|
88
|
+
"outs" => ["docs/observability_slos.md"],
|
89
|
+
"gate" => true
|
91
90
|
},
|
92
91
|
"13_DELIVERY_ROLLOUT" => {
|
93
|
-
"templates" => ["
|
94
|
-
"
|
95
|
-
"
|
96
|
-
"
|
92
|
+
"templates" => ["delivery_rollout.md"],
|
93
|
+
"description" => "Plan Delivery and Rollout",
|
94
|
+
"outs" => ["docs/delivery_rollout.md"],
|
95
|
+
"gate" => true
|
97
96
|
},
|
98
97
|
"14_DOCS_PORTAL" => {
|
99
|
-
"templates" => ["
|
100
|
-
"
|
101
|
-
"
|
102
|
-
"
|
98
|
+
"templates" => ["docs_portal.md"],
|
99
|
+
"description" => "Documentation Portal",
|
100
|
+
"outs" => ["docs/docs_portal.md"],
|
101
|
+
"gate" => false
|
103
102
|
},
|
104
103
|
"15_POST_RELEASE" => {
|
105
|
-
"templates" => ["
|
106
|
-
"
|
107
|
-
"
|
108
|
-
"
|
104
|
+
"templates" => ["post_release.md"],
|
105
|
+
"description" => "Post-Release Review",
|
106
|
+
"outs" => ["docs/post_release.md"],
|
107
|
+
"gate" => true
|
109
108
|
}
|
110
109
|
}.freeze
|
111
110
|
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Aidp
|
4
|
+
class JobManager
|
5
|
+
def initialize(project_dir)
|
6
|
+
@project_dir = project_dir
|
7
|
+
end
|
8
|
+
|
9
|
+
def create_job(job_class, args = {})
|
10
|
+
# Create a new job and return job ID
|
11
|
+
# This is a placeholder implementation
|
12
|
+
job_id = rand(1000..9999)
|
13
|
+
|
14
|
+
# Store job metadata for testing
|
15
|
+
@jobs ||= {}
|
16
|
+
@jobs[job_id] = {
|
17
|
+
id: job_id,
|
18
|
+
job_class: job_class,
|
19
|
+
args: args,
|
20
|
+
status: "queued",
|
21
|
+
created_at: Time.now
|
22
|
+
}
|
23
|
+
|
24
|
+
job_id
|
25
|
+
end
|
26
|
+
|
27
|
+
def get_job(job_id)
|
28
|
+
@jobs ||= {}
|
29
|
+
@jobs[job_id]
|
30
|
+
end
|
31
|
+
|
32
|
+
def update_job_status(job_id, status, error: nil)
|
33
|
+
@jobs ||= {}
|
34
|
+
return unless @jobs[job_id]
|
35
|
+
|
36
|
+
@jobs[job_id][:status] = status
|
37
|
+
@jobs[job_id][:error] = error if error
|
38
|
+
@jobs[job_id][:updated_at] = Time.now
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "que"
|
4
|
+
|
5
|
+
module Aidp
|
6
|
+
module Jobs
|
7
|
+
class BaseJob < Que::Job
|
8
|
+
# Default settings
|
9
|
+
self.retry_interval = 30.0 # 30 seconds between retries
|
10
|
+
self.maximum_retry_count = 3
|
11
|
+
|
12
|
+
# Error tracking
|
13
|
+
class_attribute :error_handlers
|
14
|
+
self.error_handlers = []
|
15
|
+
|
16
|
+
def self.on_error(&block)
|
17
|
+
error_handlers << block
|
18
|
+
end
|
19
|
+
|
20
|
+
protected
|
21
|
+
|
22
|
+
def log_info(message)
|
23
|
+
Que.logger.info "[#{self.class.name}] #{message}"
|
24
|
+
end
|
25
|
+
|
26
|
+
def log_error(message)
|
27
|
+
Que.logger.error "[#{self.class.name}] #{message}"
|
28
|
+
end
|
29
|
+
|
30
|
+
def handle_error(error)
|
31
|
+
self.class.error_handlers.each do |handler|
|
32
|
+
handler.call(error, self)
|
33
|
+
rescue => e
|
34
|
+
log_error "Error handler failed: #{e.message}"
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
# Override run to add error handling
|
39
|
+
def run(*args)
|
40
|
+
raise NotImplementedError, "#{self.class} must implement #run"
|
41
|
+
rescue => error
|
42
|
+
handle_error(error)
|
43
|
+
raise # Re-raise to trigger Que's retry mechanism
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|