ecoportal-api-v2 3.3.3 → 3.3.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.
data/.rubocop.yml DELETED
@@ -1,101 +0,0 @@
1
- AllCops:
2
- TargetRubyVersion: 3.2
3
- Exclude:
4
- - 'config/routes.rb'
5
- NewCops: enable
6
-
7
- Metrics/ClassLength:
8
- Max: 500
9
- Metrics/ModuleLength:
10
- Max: 300
11
- Metrics/MethodLength:
12
- Max: 50
13
- Metrics/AbcSize:
14
- Max: 30
15
- Metrics/ParameterLists:
16
- Max: 5
17
- CountKeywordArgs: false
18
- Metrics/BlockLength:
19
- CountAsOne: ['array', 'heredoc', 'method_call']
20
- Max: 50
21
- Metrics/CyclomaticComplexity:
22
- Max: 30
23
- Metrics/PerceivedComplexity:
24
- Max: 30
25
-
26
- Style/AccessorGrouping:
27
- Enabled: false
28
- Style/ConditionalAssignment:
29
- Enabled: false
30
- Style/BlockDelimiters:
31
- BracesRequiredMethods: ['log']
32
- Style/HashSyntax:
33
- EnforcedShorthandSyntax: either
34
- EnforcedStyle: no_mixed_keys
35
- Style/ArgumentsForwarding:
36
- UseAnonymousForwarding: false
37
- Style/ClassAndModuleChildren:
38
- Enabled: false
39
- Style/FrozenStringLiteralComment:
40
- Enabled: false
41
- Style/StringLiterals:
42
- Enabled: false
43
- Style/StringLiteralsInInterpolation:
44
- Enabled: false
45
- Style/Documentation:
46
- Enabled: false
47
- Style/CommentedKeyword:
48
- Enabled: false
49
- Style/MultilineBlockChain:
50
- Enabled: false
51
- Style/AndOr:
52
- Enabled: false
53
- Style/Alias:
54
- EnforcedStyle: prefer_alias_method
55
- Style/FetchEnvVar:
56
- Enabled: false
57
- Style/RegexpLiteral:
58
- EnforcedStyle: mixed
59
- AllowInnerSlashes: true
60
-
61
- Layout/HashAlignment:
62
- EnforcedColonStyle: table
63
- EnforcedHashRocketStyle: table
64
- Layout/LeadingCommentSpace:
65
- Enabled: false
66
- AllowGemfileRubyComment: true
67
- Layout/ParameterAlignment:
68
- Enabled: false
69
- Layout/MultilineMethodDefinitionBraceLayout:
70
- EnforcedStyle: symmetrical
71
- Layout/LineLength:
72
- Enabled: true
73
- Layout/SpaceInsideHashLiteralBraces:
74
- Enabled: false
75
- Layout/SpaceInsideBlockBraces:
76
- Enabled: false
77
- Layout/SpaceAroundOperators:
78
- Enabled: false
79
- Layout/ExtraSpacing:
80
- AllowForAlignment: true
81
- AllowBeforeTrailingComments: true
82
- Layout/AccessModifierIndentation:
83
- EnforcedStyle: indent
84
- Layout/DotPosition:
85
- EnforcedStyle: trailing
86
- Layout/MultilineMethodCallIndentation:
87
- EnforcedStyle: indented
88
- Layout/FirstHashElementIndentation:
89
- Enabled: false
90
- Layout/EmptyLineAfterGuardClause:
91
- Enabled: false
92
-
93
- Naming/VariableNumber:
94
- EnforcedStyle: snake_case
95
- CheckSymbols: false
96
- Naming/MethodParameterName:
97
- AllowedNames: ['x', 'y', 'i', 'j', 'id', 'io']
98
- Naming/RescuedExceptionsVariableName:
99
- Enabled: false
100
- Naming/BlockForwarding:
101
- Enabled: false
data/.ruby-version DELETED
@@ -1 +0,0 @@
1
- 3.2.2
data/.yardopts DELETED
@@ -1,10 +0,0 @@
1
- --readme README.md
2
- --charset utf-8
3
- --markup-provider=redcarpet
4
- --markup=markdown
5
- --no-private
6
- --output-dir ./doc
7
- 'lib/**/*.rb'
8
- -
9
- CHANGELOG.md
10
- LICENSE
data/CLAUDE.md DELETED
@@ -1,82 +0,0 @@
1
- # CLAUDE.md — ecoportal-api-v2
2
-
3
- AI agent instructions for this repository.
4
-
5
- **Cross-cutting architecture context lives in `ecoportal-api-graphql` — see its `CLAUDE.md` and `.claude/` folder for the full dependency map, project history, and shared skills.**
6
-
7
- ---
8
-
9
- ## Repository Role
10
-
11
- `ecoportal-api-v2` is the **REST API v2 gem**, extending `ecoportal-api` with a rich content model layer (pages, registers, people, S3 file handling). It provides the `Ecoportal::API::V2` and `Ecoportal::API::Common::Content` namespaces.
12
-
13
- **Position in chain:**
14
- ```
15
- ecoportal-api
16
-
17
- ecoportal-api-v2 ← THIS REPO
18
-
19
- ecoportal-api-graphql
20
-
21
- eco-helpers
22
- ```
23
-
24
- **Remote:** https://gitlab.ecoportal.co.nz/oscar/ecoportal-api-v2.git
25
-
26
- ---
27
-
28
- ## Key Folder Layout
29
-
30
- ```
31
- lib/ecoportal/api/
32
- common/
33
- concerns/ Benchmarkable, Threadable mixins
34
- content/ Core model infrastructure (see below)
35
- v2/
36
- page/ Page model — sections, stages, components, forces, permissions
37
- page/component/ All field types (text, number, date, selection, files, geo, etc.)
38
- pages/ Pages collection, stage results, tasks
39
- registers/ Register models — search results, stage/page results
40
- people/ V2 people endpoint
41
- s3/ S3 file upload/download infrastructure
42
- ```
43
-
44
- ### `Common::Content` — the model engine
45
-
46
- The most complex part of this gem. Key classes:
47
-
48
- - `DoubleModel` — two-layer model (a "live" doc and a "patch" doc) with attribute DSL, diffing, nesting, and change tracking. Almost all V2 models inherit from this.
49
- - `CollectionModel` — ordered collection with upsert/delete/mutation support.
50
- - `ClassHelpers` — `class_resolver` macro for injectable class references (pattern shared with `ecoportal-api-graphql`).
51
- - `DocHelpers`, `ModelHelpers`, `IncluderHelpers` — utility mixins for model construction.
52
-
53
- ---
54
-
55
- ## Namespace
56
-
57
- - `Ecoportal::API::V2` — v2 REST resources
58
- - `Ecoportal::API::Common::Content` — shared model infrastructure (consumed downstream)
59
- - `Ecoportal::API::Common::Concerns` — threading and benchmarking mixins
60
-
61
- ---
62
-
63
- ## Key Concerns
64
-
65
- - `DoubleModel` is the foundational abstraction — understand it before modifying any V2 model.
66
- - `Common::Content::ClassHelpers` mirrors the `ClassHelpers` in `ecoportal-api-graphql` — changes to one may need mirroring in the other.
67
- - S3 upload is async with polling (`s3/files/poll.rb`) — timing-sensitive code.
68
-
69
- ---
70
-
71
- ## Backwards Compatibility
72
-
73
- `eco-helpers` and `ecoportal-api-graphql` both depend on this gem. Changes to `Common::Content::*` or `V2::Page::*` interfaces must be checked against usage in both downstream gems before committing.
74
-
75
- ---
76
-
77
- ## Running Tests
78
-
79
- ```bash
80
- bundle install
81
- bundle exec rspec
82
- ```
data/Gemfile DELETED
@@ -1,6 +0,0 @@
1
- source "https://rubygems.org"
2
-
3
- git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
4
-
5
- # Specify your gem's dependencies in ecoportal-api.gemspec
6
- gemspec
data/Rakefile DELETED
@@ -1,36 +0,0 @@
1
- require "bundler/gem_tasks"
2
- require "rspec/core/rake_task"
3
- require 'rubocop/rake_task'
4
- require "yard"
5
- require "redcarpet"
6
-
7
- desc "run the specs"
8
- RSpec::Core::RakeTask.new(:spec)
9
-
10
- desc "run rspec showing backtrace"
11
- RSpec::Core::RakeTask.new(:spec_trace) do |task|
12
- task.rspec_opts = ['--backtrace']
13
- end
14
-
15
- desc "run rspec stopping on first fail, and show backtrace"
16
- RSpec::Core::RakeTask.new(:spec_fast) do |task|
17
- task.rspec_opts = ['--fail-fast', '--backtrace']
18
- end
19
-
20
- desc "run rubocop diaplying cop names"
21
- RuboCop::RakeTask.new(:rubocop) do |t|
22
- t.options = ['--display-cop-names']
23
- end
24
-
25
- # default task name is yard
26
- desc "Yard: generate all the documentation"
27
- YARD::Rake::YardocTask.new(:doc) do |t|
28
- #t.files = ['lib/**/*.rb']
29
- end
30
-
31
- desc "default task: runs rubocop and rspec"
32
- task :default do
33
- Rake::Task[:rubocop].invoke
34
- ensure
35
- Rake::Task[:spec].invoke
36
- end
data/bin/console DELETED
@@ -1,14 +0,0 @@
1
- #!/usr/bin/env ruby
2
-
3
- require "bundler/setup"
4
- require "ecoportal/api-v2"
5
-
6
- # You can add fixtures and/or initialization code here to make experimenting
7
- # with your gem easier. You can also use a different console, if you like.
8
-
9
- # (If you use this, don't forget to add pry to your Gemfile!)
10
- # require "pry"
11
- # Pry.start
12
-
13
- require "pry"
14
- Pry.start(__FILE__)
data/bin/setup DELETED
@@ -1,8 +0,0 @@
1
- #!/usr/bin/env bash
2
- set -euo pipefail
3
- IFS=$'\n\t'
4
- set -vx
5
-
6
- bundle install
7
-
8
- # Do any other automated setup that you need to do here
@@ -1,40 +0,0 @@
1
- # rubocop:disable Gemspec/DevelopmentDependencies
2
- lib = File.expand_path('lib', __dir__)
3
- $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
-
5
- require 'ecoportal/api/v2_version'
6
-
7
- Gem::Specification.new do |spec|
8
- spec.name = 'ecoportal-api-v2'
9
- spec.version = Ecoportal::API::GEM2_VERSION
10
- spec.authors = ['Oscar Segura']
11
- spec.email = ['oscar@ecoportal.co.nz']
12
-
13
- spec.summary = "A collection of helpers for interacting with the ecoPortal MS's V2 API"
14
- spec.homepage = "https://www.ecoportal.com"
15
- spec.licenses = %w[MIT]
16
-
17
- spec.metadata['rubygems_mfa_required'] = 'true'
18
-
19
- spec.required_ruby_version = '>= 3.2.2'
20
-
21
- spec.files = `git ls-files -z`.split("\x0").reject do |f|
22
- f.match(%r{^(test|spec|features)/})
23
- end
24
- spec.bindir = 'exe'
25
- spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
26
- spec.require_paths = ['lib']
27
-
28
- spec.add_development_dependency 'pry', '~> 0.14'
29
- spec.add_development_dependency 'rake', '>= 13.0.3', '< 14'
30
- spec.add_development_dependency 'redcarpet', '>= 3.6.0', '< 4'
31
- spec.add_development_dependency 'rspec', '>= 3.12.0', '< 4'
32
- spec.add_development_dependency 'rubocop', '~> 1'
33
- spec.add_development_dependency 'rubocop-rake', '~> 0'
34
- spec.add_development_dependency 'yard', '~> 0.9'
35
-
36
- spec.add_dependency 'ecoportal-api', '~> 0.10', '>= 0.10.16'
37
- spec.add_dependency 'mime-types', '~> 3.5', '>= 3.5.2'
38
- end
39
-
40
- # rubocop:enable Gemspec/DevelopmentDependencies
@@ -1,307 +0,0 @@
1
- #!/usr/bin/env bash
2
- # auto-worker-scheduler.sh
3
- # Scheduler for auto-todo-worker. Called by the Claude Code Stop hook.
4
- # Reads local config, checks token budget and conflict guard, then
5
- # launches the worker as a background process if all gates pass.
6
- #
7
- # Log format: TIMESTAMP | ACTION | REASON
8
- # Log file: .ai-assistance/local/auto-worker-changes.log
9
- #
10
- # Usage: bash scripts/auto-worker-scheduler.sh
11
- # (called automatically by Stop hook — no arguments)
12
- #
13
- # Requires: jq (for JSON parsing). Falls back to grep/sed on missing jq.
14
-
15
- set -euo pipefail
16
-
17
- # ---------------------------------------------------------------------------
18
- # Paths (all relative — script must be run from repo root)
19
- # ---------------------------------------------------------------------------
20
- TOKEN_BUDGET_JSON=".ai-assistance/token-budget.json"
21
- LAST_RUN_JSON=".ai-assistance/local/auto-worker-last-run.json"
22
- SESSION_LOCK=".ai-assistance/local/session.lock"
23
- CHANGES_LOG=".ai-assistance/local/auto-worker-changes.log"
24
-
25
- # ---------------------------------------------------------------------------
26
- # Helpers
27
- # ---------------------------------------------------------------------------
28
-
29
- # ai_log_usage <component> <action> <detail> [session_id]
30
- # Inline copy — scheduler is self-contained; common.sh is not sourced here.
31
- # Never fails fatally; all errors suppressed.
32
- ai_log_usage() {
33
- local component="${1:-unknown}"
34
- local action="${2:-run}"
35
- local detail="${3:-}"
36
- local session_id="${4:-}"
37
- local log_dir=".ai-assistance/local/kpi"
38
- local week
39
- week=$(date +%Y-W%V 2>/dev/null || echo "unknown")
40
- local log_file="${log_dir}/usage-${week}.jsonl"
41
- local ts
42
- ts=$(date -u +%Y-%m-%dT%H:%M:%SZ 2>/dev/null || echo "unknown")
43
- mkdir -p "$log_dir" 2>/dev/null || true
44
- printf '{"component":"%s","action":"%s","detail":"%s","ts":"%s","session_id":"%s"}\n' \
45
- "$component" "$action" "$detail" "$ts" "$session_id" >> "$log_file" 2>/dev/null || true
46
- }
47
-
48
- log_action() {
49
- local action="$1"
50
- local reason="$2"
51
- local ts
52
- ts=$(date -u +"%Y-%m-%dT%H:%M:%SZ" 2>/dev/null || date +"%Y-%m-%dT%H:%M:%SZ")
53
- mkdir -p "$(dirname "$CHANGES_LOG")"
54
- printf "%s | %s | %s\n" "$ts" "$action" "$reason" >> "$CHANGES_LOG"
55
- }
56
-
57
- # Read a JSON field using jq if available, otherwise grep+sed fallback.
58
- # Usage: json_field <file> <dotted.key> e.g. json_field config.json auto_worker.enabled
59
- json_field() {
60
- local file="$1"
61
- local key="$2"
62
- if command -v jq >/dev/null 2>&1; then
63
- jq -r ".${key} // empty" "$file" 2>/dev/null
64
- else
65
- # Minimal fallback: grep for the last segment key, extract bare value.
66
- local leaf
67
- leaf=$(printf '%s' "$key" | sed 's/.*\.//')
68
- grep -o "\"${leaf}\"[[:space:]]*:[[:space:]]*[^,}]*" "$file" 2>/dev/null \
69
- | tail -1 \
70
- | sed 's/.*:[[:space:]]*//' \
71
- | tr -d '"' \
72
- | tr -d ' '
73
- fi
74
- }
75
-
76
- # ---------------------------------------------------------------------------
77
- # Gate 1: auto_worker.enabled
78
- # ---------------------------------------------------------------------------
79
-
80
- if [ ! -f "$TOKEN_BUDGET_JSON" ]; then
81
- log_action "SKIP" "token-budget.json not found — auto_worker not configured"
82
- ai_log_usage "script/auto-worker-scheduler" "skip" "auto_worker not enabled -- token-budget.json not found"
83
- exit 0
84
- fi
85
-
86
- enabled=$(json_field "$TOKEN_BUDGET_JSON" "auto_worker.enabled")
87
- if [ "$enabled" != "true" ]; then
88
- log_action "SKIP" "auto_worker.enabled is false or absent in token-budget.json"
89
- ai_log_usage "script/auto-worker-scheduler" "skip" "auto_worker not enabled"
90
- exit 0
91
- fi
92
-
93
- # ---------------------------------------------------------------------------
94
- # Gate 2: Conflict guard — check for active Claude session via session.lock
95
- # ---------------------------------------------------------------------------
96
-
97
- if [ -f "$SESSION_LOCK" ]; then
98
- lock_content=""
99
- if [ -r "$SESSION_LOCK" ]; then
100
- lock_content=$(cat "$SESSION_LOCK" 2>/dev/null || true)
101
- fi
102
- log_action "SKIP" "Active Claude session detected (session.lock present: ${lock_content})"
103
- ai_log_usage "script/auto-worker-scheduler" "skip" "session.lock present"
104
- exit 0
105
- fi
106
-
107
- # ---------------------------------------------------------------------------
108
- # Gate 3: Token budget threshold check
109
- # ---------------------------------------------------------------------------
110
- # Find the current ISO week KPI file. Format: weekly-YYYY-WNN.json
111
- # We compute ISO week ourselves for portability.
112
-
113
- iso_week() {
114
- # Returns YYYY-WNN string for today (ISO 8601 week numbering)
115
- if command -v python3 >/dev/null 2>&1; then
116
- python3 -c "
117
- import datetime
118
- today = datetime.date.today()
119
- iso = today.isocalendar()
120
- print('%04d-W%02d' % (iso[0], iso[1]))
121
- "
122
- elif command -v python >/dev/null 2>&1; then
123
- python -c "
124
- import datetime
125
- today = datetime.date.today()
126
- iso = today.isocalendar()
127
- print('%04d-W%02d' % (iso[0], iso[1]))
128
- "
129
- else
130
- # Fallback: use date command Week-of-year (not strictly ISO but close enough)
131
- date +"%Y-W%V" 2>/dev/null || date +"%Y-W%U"
132
- fi
133
- }
134
-
135
- KPI_DIR=".ai-assistance/local/kpi"
136
- WEEK_KEY=$(iso_week)
137
- KPI_FILE="${KPI_DIR}/weekly-${WEEK_KEY}.json"
138
-
139
- # Read quota and usage from KPI file (if present)
140
- weekly_tokens_used=0
141
- weekly_quota=0
142
- window_start_ts="" # ISO timestamp of first session start in current 5-hour window
143
-
144
- if [ -f "$KPI_FILE" ] && command -v jq >/dev/null 2>&1; then
145
- # Sum tokens_used across all sessions
146
- weekly_tokens_used=$(jq '[.sessions[]?.tokens_used // 0] | add // 0' "$KPI_FILE" 2>/dev/null || echo "0")
147
- weekly_quota=$(jq -r '.quota // 0' "$KPI_FILE" 2>/dev/null || echo "0")
148
-
149
- # Find most recent session start to compute reset window
150
- window_start_ts=$(jq -r '
151
- [.sessions[]?.session_start // empty] | sort | last // empty
152
- ' "$KPI_FILE" 2>/dev/null || true)
153
- fi
154
-
155
- # Read quota from token-budget.json as fallback
156
- if [ "$weekly_quota" = "0" ] || [ -z "$weekly_quota" ]; then
157
- quota_from_config=$(json_field "$TOKEN_BUDGET_JSON" "weekly_quota.total_tokens")
158
- if [ "$quota_from_config" != "null" ] && [ -n "$quota_from_config" ]; then
159
- weekly_quota="$quota_from_config"
160
- fi
161
- fi
162
-
163
- # If no quota is set (null), skip the threshold gate entirely
164
- if [ "$weekly_quota" = "0" ] || [ "$weekly_quota" = "null" ] || [ -z "$weekly_quota" ]; then
165
- log_action "OK" "No weekly quota set — skipping token threshold gate"
166
- else
167
- # Calculate usage percentage
168
- if command -v python3 >/dev/null 2>&1; then
169
- usage_pct=$(python3 -c "
170
- used = float('${weekly_tokens_used}' or '0')
171
- quota = float('${weekly_quota}')
172
- print('%.1f' % (used / quota * 100) if quota > 0 else '0')
173
- ")
174
- else
175
- usage_pct=0
176
- fi
177
-
178
- # Determine time remaining in current 5-hour reset window
179
- # 5-hour window = 18000 seconds
180
- window_seconds_elapsed=0
181
- if [ -n "$window_start_ts" ] && command -v python3 >/dev/null 2>&1; then
182
- window_seconds_elapsed=$(python3 -c "
183
- import datetime, sys
184
- try:
185
- ts = '${window_start_ts}'.replace('Z', '+00:00')
186
- start = datetime.datetime.fromisoformat(ts)
187
- now = datetime.datetime.now(datetime.timezone.utc)
188
- elapsed = (now - start).total_seconds()
189
- print(int(max(0, elapsed)))
190
- except Exception:
191
- print(0)
192
- ")
193
- fi
194
-
195
- WINDOW_DURATION=18000 # 5 hours in seconds
196
- window_remaining=$(( WINDOW_DURATION - window_seconds_elapsed ))
197
- if [ "$window_remaining" -lt 0 ]; then
198
- window_remaining=0
199
- fi
200
- window_remaining_min=$(( window_remaining / 60 ))
201
-
202
- # Determine threshold based on time remaining in window (ADR-007 table)
203
- # >120 min -> 65%; 60-120 -> 75%; 45-60 -> 80%; 30-45 -> 90%; 15-30 -> 95%; <15 -> skip
204
- if [ "$window_remaining_min" -lt 15 ]; then
205
- log_action "SKIP" "Less than 15 min remaining in token reset window — will not launch"
206
- ai_log_usage "script/auto-worker-scheduler" "skip" "token budget -- less than 15 min remaining in reset window"
207
- exit 0
208
- elif [ "$window_remaining_min" -lt 30 ]; then
209
- threshold=95
210
- elif [ "$window_remaining_min" -lt 45 ]; then
211
- threshold=90
212
- elif [ "$window_remaining_min" -lt 60 ]; then
213
- threshold=80
214
- elif [ "$window_remaining_min" -lt 120 ]; then
215
- threshold=75
216
- else
217
- threshold=65
218
- fi
219
-
220
- # Compare usage_pct against threshold (integer comparison via python3)
221
- if command -v python3 >/dev/null 2>&1; then
222
- over_threshold=$(python3 -c "
223
- pct = float('${usage_pct}' or '0')
224
- thr = float('${threshold}')
225
- print('true' if pct >= thr else 'false')
226
- ")
227
- else
228
- over_threshold="false"
229
- fi
230
-
231
- if [ "$over_threshold" = "true" ]; then
232
- log_action "SKIP" "Token usage ${usage_pct}% >= threshold ${threshold}% (${window_remaining_min} min remaining in window)"
233
- ai_log_usage "script/auto-worker-scheduler" "skip" "token budget at ${usage_pct}% (threshold ${threshold}%)"
234
- exit 0
235
- fi
236
-
237
- log_action "OK" "Token gate passed: ${usage_pct}% used, threshold ${threshold}% (${window_remaining_min} min remaining)"
238
- fi
239
-
240
- # ---------------------------------------------------------------------------
241
- # Gate 4: Determine cadence (informational — actual cron cadence managed by
242
- # CronCreate; this script just logs the recommendation)
243
- # ---------------------------------------------------------------------------
244
-
245
- recommended_cadence="2hr"
246
- if [ -f "$LAST_RUN_JSON" ] && command -v jq >/dev/null 2>&1; then
247
- completed_count=$(jq '.completed | length' "$LAST_RUN_JSON" 2>/dev/null || echo "0")
248
- if [ "$completed_count" -gt "0" ]; then
249
- recommended_cadence="30min"
250
- fi
251
- fi
252
-
253
- # ---------------------------------------------------------------------------
254
- # Gate 5: dangerously_skip_permissions opt-in
255
- # ---------------------------------------------------------------------------
256
-
257
- skip_perms=$(json_field "$TOKEN_BUDGET_JSON" "auto_worker.dangerously_skip_permissions")
258
- AUTO_WORKER_PROMPT="Run the auto-worker for this repo.
259
-
260
- Instructions:
261
- 1. Read TODO.md and collect all items tagged \`[auto]\` or \`[auto:last]\`.
262
- 2. If no \`[auto]\` items exist, exit cleanly with a brief log entry.
263
- 3. Plan execution order as a dependency graph — do NOT follow TODO list order.
264
- - Items without dependencies on each other may run in parallel (branch out when the work is self-contained enough to isolate).
265
- - \`[auto:last]\` items run only after all \`[auto]\` items complete or are skipped.
266
- 4. For each item:
267
- - Execute the task.
268
- - Mark it \`[x]\` in TODO.md on completion.
269
- - Add any discovered follow-up tasks as new TODO items; tag \`[auto]\` only if they meet the eligibility criteria in standards/tooling/auto-worker-eligibility.md.
270
- 5. Do NOT touch items that are not tagged \`[auto]\` or \`[auto:last]\`.
271
- 6. At the end, write a one-paragraph summary of what was done, skipped, and added to TODO."
272
-
273
- CLAUDE_FLAGS="--print \"${AUTO_WORKER_PROMPT}\""
274
- if [ "$skip_perms" = "true" ]; then
275
- CLAUDE_FLAGS="--dangerously-skip-permissions --print \"${AUTO_WORKER_PROMPT}\""
276
- fi
277
-
278
- # ---------------------------------------------------------------------------
279
- # Launch: run auto-todo-worker as a background process
280
- # ---------------------------------------------------------------------------
281
-
282
- log_action "LAUNCH" "Launching auto-todo-worker (cadence recommendation: ${recommended_cadence})"
283
- ai_log_usage "script/auto-worker-scheduler" "launch" "launching auto-todo-worker"
284
-
285
- if [[ "${OSTYPE:-}" == "msys" ]] || [[ "${OSTYPE:-}" == "cygwin" ]] || \
286
- [[ "${OS:-}" == "Windows_NT" ]]; then
287
- # Windows via Git Bash / WSL
288
- # Use PowerShell Start-Process to launch detached from the current terminal
289
- powershell -Command "Start-Process -FilePath 'claude' \
290
- -ArgumentList '${CLAUDE_FLAGS}' \
291
- -WorkingDirectory '${PWD}' \
292
- -NoNewWindow" &
293
- launch_pid=$!
294
- else
295
- # Unix / macOS / WSL2
296
- if [ "$skip_perms" = "true" ]; then
297
- nohup claude --dangerously-skip-permissions --print "run auto worker" \
298
- > ".ai-assistance/local/auto-worker-stdout.log" 2>&1 &
299
- else
300
- nohup claude --print "run auto worker" \
301
- > ".ai-assistance/local/auto-worker-stdout.log" 2>&1 &
302
- fi
303
- launch_pid=$!
304
- fi
305
-
306
- log_action "LAUNCHED" "Background PID ${launch_pid:-unknown} — flags: ${CLAUDE_FLAGS}"
307
- exit 0