lex-factory 0.1.0 → 0.1.4

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: df3fc328fe57d371e7b812ac1285a75dd022e38c7c33612e21292ddabd15de4c
4
- data.tar.gz: 7722b25f034cc3aaea6d8e6963f47907dec10bf27831d3afbf9bf633c12bc84f
3
+ metadata.gz: d9fd09964743d7a47df24981945f31c86bbceed1342f6d83e1d2f9e02c04e0a2
4
+ data.tar.gz: f3d00a404776a1da2f240dd8258159168765c08c7bd915df7cb22caed3cd101d
5
5
  SHA512:
6
- metadata.gz: da95c3eedec07c08767cedf334bc7d96efbe4fe2aeeb3ce788c90121f8195cc697218b45f0624022bd7e6a60c868799f378ba6c79669cea31d647d870d74ec95
7
- data.tar.gz: 692244c9e64ca57b461433dffb757122fa68cc7eb670cc7ac2fbedfdc9651a7f31c8dd65ba9179696a60ffba70dd70e4985fed389663b899bede586638ca9dff
6
+ metadata.gz: f7163419e296bbb0590ac07662ed89f40984aa24949dcc0ffdde31a73c67b5d4b4aefb727281dc4d22c767bd457f5839ef3c9467cdcd37aedda684bd892afebd
7
+ data.tar.gz: 9d3dc61bf03a503f86adab62f4329571e7c9e4b193734e697b42c43c78afce01a5227bae7f8a27538ef35d7fef84ceb9bd7318e7e806fffa42ac69e6d8c748cd
data/CHANGELOG.md CHANGED
@@ -1,5 +1,27 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.1.4] - 2026-03-30
4
+
5
+ ### Changed
6
+ - update to rubocop-legion 0.1.7, resolve all offenses
7
+
8
+ ## [0.1.3] - 2026-03-29
9
+
10
+ ### Changed
11
+ - `run_pipeline` and `pipeline_status` accept `**` for task system payload compatibility
12
+
13
+ ## [0.1.2] - 2026-03-28
14
+
15
+ ### Changed
16
+ - Implement develop stage via real LLM code generation, delegating to `lex-codegen` `FromGap.generate`
17
+ - Graceful fallback to stub strategy when lex-codegen is not loaded
18
+ - Exclude `pipeline_runner.rb` from `Metrics/ClassLength` and `Metrics/AbcSize` in rubocop config
19
+
20
+ ## [0.1.1] - 2026-03-26
21
+
22
+ ### Changed
23
+ - fix remote_invocable? to use class method for local dispatch
24
+
3
25
  ## [0.1.0] - 2026-03-24
4
26
 
5
27
  ### Added
@@ -9,9 +9,9 @@ module Legion
9
9
 
10
10
  SCORE_WEIGHTS = {
11
11
  completeness: 0.35,
12
- correctness: 0.35,
13
- quality: 0.20,
14
- security: 0.10
12
+ correctness: 0.35,
13
+ quality: 0.20,
14
+ security: 0.10
15
15
  }.freeze
16
16
 
17
17
  DEFAULT_SATISFACTION_THRESHOLD = 0.8
@@ -11,9 +11,9 @@ module Legion
11
11
  threshold: Constants::DEFAULT_SATISFACTION_THRESHOLD)
12
12
  scores = {
13
13
  completeness: clamp(completeness),
14
- correctness: clamp(correctness),
15
- quality: clamp(quality),
16
- security: clamp(security)
14
+ correctness: clamp(correctness),
15
+ quality: clamp(quality),
16
+ security: clamp(security)
17
17
  }
18
18
 
19
19
  aggregate = Constants::SCORE_WEIGHTS.sum do |dimension, weight|
@@ -21,10 +21,10 @@ module Legion
21
21
  end
22
22
 
23
23
  {
24
- pass: aggregate >= threshold,
24
+ pass: aggregate >= threshold,
25
25
  aggregate: aggregate.round(4),
26
26
  threshold: threshold,
27
- scores: scores
27
+ scores: scores
28
28
  }
29
29
  end
30
30
 
@@ -16,12 +16,12 @@ module Legion
16
16
  code_blocks = extract_code_blocks(content)
17
17
 
18
18
  {
19
- success: true,
20
- file_path: file_path,
21
- title: title,
22
- sections: sections,
19
+ success: true,
20
+ file_path: file_path,
21
+ title: title,
22
+ sections: sections,
23
23
  code_blocks: code_blocks,
24
- raw: content
24
+ raw: content
25
25
  }
26
26
  rescue StandardError => e
27
27
  { success: false, error: e.message }
@@ -1,12 +1,15 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'json'
3
+ require 'legion/json'
4
4
  require 'fileutils'
5
5
 
6
6
  module Legion
7
7
  module Extensions
8
8
  module Factory
9
9
  class PipelineRunner
10
+ include Legion::JSON::Helper
11
+ include Legion::Logging::Helper
12
+
10
13
  attr_reader :spec_path, :output_dir
11
14
 
12
15
  def initialize(spec_path:, output_dir: nil, threshold: nil, max_retries: nil)
@@ -34,20 +37,21 @@ module Legion
34
37
  end
35
38
 
36
39
  {
37
- success: true,
40
+ success: true,
38
41
  stages_completed: @context[:completed_stages].size,
39
- output_dir: @output_dir
42
+ output_dir: @output_dir
40
43
  }
41
44
  rescue StandardError => e
45
+ log.error "PipelineRunner#run failed at stage #{@context[:current_stage]}: #{e.message}"
42
46
  save_state
43
47
  { success: false, error: e.message, last_stage: @context[:current_stage] }
44
48
  end
45
49
 
46
50
  def status
47
51
  {
48
- spec_path: @spec_path,
49
- output_dir: @output_dir,
50
- current_stage: @context[:current_stage],
52
+ spec_path: @spec_path,
53
+ output_dir: @output_dir,
54
+ current_stage: @context[:current_stage],
51
55
  completed_stages: @context[:completed_stages] || []
52
56
  }
53
57
  end
@@ -59,9 +63,9 @@ module Legion
59
63
  ctx[:spec] = parsed
60
64
  ctx[:raw_spec] = Helpers::SpecParser.raw_content(file_path: @spec_path)
61
65
  ctx[:discover] = {
62
- title: parsed[:title],
63
- sections: parsed[:sections],
64
- code_blocks: parsed[:code_blocks],
66
+ title: parsed[:title],
67
+ sections: parsed[:sections],
68
+ code_blocks: parsed[:code_blocks],
65
69
  requirements: extract_requirements(parsed)
66
70
  }
67
71
  ctx
@@ -70,7 +74,7 @@ module Legion
70
74
  def stage_define(ctx)
71
75
  requirements = ctx.dig(:discover, :requirements) || []
72
76
  ctx[:define] = {
73
- tasks: requirements.map.with_index(1) { |req, i| { id: i, requirement: req, status: :pending } },
77
+ tasks: requirements.map.with_index(1) { |req, i| { id: i, requirement: req, status: :pending } },
74
78
  task_count: requirements.size
75
79
  }
76
80
  ctx
@@ -78,11 +82,14 @@ module Legion
78
82
 
79
83
  def stage_develop(ctx)
80
84
  tasks = ctx.dig(:define, :tasks) || []
81
- tasks.each { |t| t[:status] = :completed }
82
- ctx[:develop] = {
83
- tasks_completed: tasks.size,
84
- tasks_failed: 0
85
- }
85
+
86
+ if codegen_available?
87
+ develop_with_codegen(ctx, tasks)
88
+ else
89
+ tasks.each { |t| t[:status] = :completed }
90
+ ctx[:develop] = { tasks_completed: tasks.size, tasks_failed: 0, strategy: :stub }
91
+ end
92
+
86
93
  ctx
87
94
  end
88
95
 
@@ -93,19 +100,72 @@ module Legion
93
100
 
94
101
  gate_result = Helpers::QualityGate.score(
95
102
  completeness: completeness,
96
- correctness: 1.0,
97
- quality: 1.0,
98
- security: 1.0,
99
- threshold: @threshold
103
+ correctness: 1.0,
104
+ quality: 1.0,
105
+ security: 1.0,
106
+ threshold: @threshold
100
107
  )
101
108
 
102
109
  ctx[:deliver] = {
103
110
  gate_result: gate_result,
104
- summary: "Pipeline complete: #{tasks_completed}/#{tasks_total} tasks"
111
+ summary: "Pipeline complete: #{tasks_completed}/#{tasks_total} tasks"
105
112
  }
106
113
  ctx
107
114
  end
108
115
 
116
+ def codegen_available?
117
+ defined?(Legion::Extensions::Codegen::Runners::FromGap) &&
118
+ Legion::Extensions::Codegen::Runners::FromGap.respond_to?(:generate)
119
+ end
120
+
121
+ def develop_with_codegen(ctx, tasks)
122
+ counters = { completed: 0, failed: 0 }
123
+ artifacts = []
124
+
125
+ tasks.each { |task| run_codegen_task(task, counters, artifacts) }
126
+
127
+ ctx[:develop] = build_develop_context(ctx, counters, artifacts)
128
+ end
129
+
130
+ def run_codegen_task(task, counters, artifacts)
131
+ result = Legion::Extensions::Codegen::Runners::FromGap.generate(
132
+ gap: { id: task[:id], type: :runner_method, intent: task[:requirement] }
133
+ )
134
+ apply_task_result(task, result, counters, artifacts)
135
+ rescue StandardError => e
136
+ log.error "PipelineRunner#run_codegen_task failed for task #{task[:id]}: #{e.message}"
137
+ task[:status] = :failed
138
+ task[:reason] = e.message
139
+ counters[:failed] += 1
140
+ end
141
+
142
+ def apply_task_result(task, result, counters, artifacts)
143
+ if result[:success]
144
+ task[:status] = :completed
145
+ task[:generation_id] = result[:generation_id]
146
+ task[:file_path] = result[:file_path]
147
+ artifacts << result
148
+ counters[:completed] += 1
149
+ else
150
+ task[:status] = :failed
151
+ task[:reason] = result[:reason]
152
+ counters[:failed] += 1
153
+ end
154
+ end
155
+
156
+ def build_develop_context(ctx, counters, artifacts)
157
+ {
158
+ tasks_completed: counters[:completed],
159
+ tasks_failed: counters[:failed],
160
+ strategy: :codegen,
161
+ spec_title: ctx.dig(:discover, :title) || 'unknown',
162
+ spec_length: (ctx[:raw_spec] || '').length,
163
+ artifacts: artifacts.map do |a|
164
+ { generation_id: a[:generation_id], tier: a[:tier], file_path: a[:file_path] }
165
+ end
166
+ }
167
+ end
168
+
109
169
  def extract_requirements(parsed)
110
170
  return [] unless parsed[:success]
111
171
 
@@ -120,19 +180,21 @@ module Legion
120
180
 
121
181
  def save_state
122
182
  ::FileUtils.mkdir_p(@output_dir)
123
- File.write(state_file_path, ::JSON.generate(serialize_context(@context)))
124
- rescue StandardError
183
+ File.write(state_file_path, json_dump(serialize_context(@context)))
184
+ rescue StandardError => e
185
+ log.warn "PipelineRunner#save_state failed: #{e.message}"
125
186
  nil
126
187
  end
127
188
 
128
189
  def load_state
129
190
  return default_context unless File.exist?(state_file_path)
130
191
 
131
- data = ::JSON.parse(File.read(state_file_path), symbolize_names: true)
192
+ data = json_load(File.read(state_file_path))
132
193
  data[:completed_stages] = (data[:completed_stages] || []).map(&:to_sym)
133
194
  data[:current_stage] = data[:current_stage]&.to_sym
134
195
  data
135
- rescue StandardError
196
+ rescue StandardError => e
197
+ log.warn "PipelineRunner#load_state failed: #{e.message}"
136
198
  default_context
137
199
  end
138
200
 
@@ -161,7 +223,8 @@ module Legion
161
223
  return {} unless defined?(Legion::Settings) && !Legion::Settings[:factory].nil?
162
224
 
163
225
  Legion::Settings[:factory] || {}
164
- rescue StandardError
226
+ rescue StandardError => e
227
+ log.debug "PipelineRunner#factory_settings failed: #{e.message}"
165
228
  {}
166
229
  end
167
230
  end
@@ -4,10 +4,10 @@ module Legion
4
4
  module Extensions
5
5
  module Factory
6
6
  module Runners
7
- module Factory
7
+ module Factory # rubocop:disable Legion/Extension/RunnerIncludeHelpers
8
8
  module_function
9
9
 
10
- def run_pipeline(spec_path:, output_dir: nil)
10
+ def run_pipeline(spec_path:, output_dir: nil, **)
11
11
  return { success: false, error: 'spec file not found' } unless ::File.exist?(spec_path)
12
12
 
13
13
  runner = PipelineRunner.new(spec_path: spec_path, output_dir: output_dir)
@@ -16,7 +16,7 @@ module Legion
16
16
  { success: false, error: e.message }
17
17
  end
18
18
 
19
- def pipeline_status(output_dir:)
19
+ def pipeline_status(output_dir:, **)
20
20
  runner = PipelineRunner.new(spec_path: '', output_dir: output_dir)
21
21
  status = runner.status
22
22
  { success: true, **status }
@@ -3,7 +3,7 @@
3
3
  module Legion
4
4
  module Extensions
5
5
  module Factory
6
- VERSION = '0.1.0'
6
+ VERSION = '0.1.4'
7
7
  end
8
8
  end
9
9
  end
@@ -10,7 +10,7 @@ require_relative 'factory/runners/factory'
10
10
  module Legion
11
11
  module Extensions
12
12
  module Factory
13
- extend Legion::Extensions::Core if Legion::Extensions.const_defined?(:Core)
13
+ extend Legion::Extensions::Core if Legion::Extensions.const_defined?(:Core, false)
14
14
 
15
15
  class << self
16
16
  def data_required?
@@ -20,6 +20,10 @@ module Legion
20
20
  def llm_required?
21
21
  true
22
22
  end
23
+
24
+ def remote_invocable?
25
+ false
26
+ end
23
27
  end
24
28
  end
25
29
  end
metadata CHANGED
@@ -1,14 +1,42 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: lex-factory
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.1.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Esity
8
8
  bindir: bin
9
9
  cert_chain: []
10
10
  date: 1980-01-02 00:00:00.000000000 Z
11
- dependencies: []
11
+ dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: legion-json
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - ">="
17
+ - !ruby/object:Gem::Version
18
+ version: '1.2'
19
+ type: :runtime
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - ">="
24
+ - !ruby/object:Gem::Version
25
+ version: '1.2'
26
+ - !ruby/object:Gem::Dependency
27
+ name: legion-logging
28
+ requirement: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - ">="
31
+ - !ruby/object:Gem::Version
32
+ version: '0.3'
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - ">="
38
+ - !ruby/object:Gem::Version
39
+ version: '0.3'
12
40
  description: Double Diamond pipeline that takes a specification and produces working
13
41
  code with tests
14
42
  email: