pwn 0.5.783 → 0.5.784
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/Gemfile +3 -3
- data/Rakefile +19 -1
- data/bin/pwn_setup +1 -1
- data/build_gem.sh +1 -1
- data/documentation/Backup-Retention.md +27 -0
- data/documentation/Cron.md +64 -35
- data/documentation/How-PWN-Works.md +1 -1
- data/documentation/Installation.md +247 -6
- data/documentation/Persistence.md +1 -1
- data/documentation/Reinforcement-Learning.md +5 -6
- data/documentation/diagrams/cron-scheduling.svg +130 -128
- data/documentation/diagrams/dot/cron-scheduling.dot +3 -3
- data/etc/default_cron/session_cleanup.rb +1217 -10
- data/lib/pwn/ai/agent/extrospection.rb +6 -7
- data/lib/pwn/ai/agent/learning.rb +48 -7
- data/lib/pwn/ai/agent/policy.rb +5 -0
- data/lib/pwn/ai/agent/swarm.rb +31 -16
- data/lib/pwn/ai/router.rb +45 -2
- data/lib/pwn/cron.rb +120 -49
- data/lib/pwn/migrate.rb +292 -15
- data/lib/pwn/plugins/binary_parser.rb +5 -4
- data/lib/pwn/plugins/ghidra.rb +4 -3
- data/lib/pwn/plugins/log.rb +18 -2
- data/lib/pwn/plugins/repl/ai.rb +12 -1
- data/lib/pwn/plugins/repl/ai_console.rb +6 -2
- data/lib/pwn/plugins/repl/ai_swarm.rb +4 -4
- data/lib/pwn/sessions.rb +58 -16
- data/lib/pwn/setup.rb +9 -17
- data/lib/pwn/version.rb +1 -1
- data/scripts/build_gate.rb +58 -0
- data/spec/integration/build_gate_spec.rb +73 -0
- data/spec/integration/persistence_roundtrip_spec.rb +1 -7
- data/spec/lib/pwn/ai/agent/policy_spec.rb +27 -0
- data/spec/lib/pwn/ai/router_spec.rb +30 -2
- data/spec/lib/pwn/cron_spec.rb +113 -6
- data/spec/lib/pwn/migrate_spec.rb +160 -5
- data/spec/lib/pwn/plugins/log_spec.rb +7 -0
- data/spec/lib/pwn/plugins/repl/ai_console_spec.rb +16 -4
- data/spec/lib/pwn/plugins/repl/ai_swarm_spec.rb +11 -0
- data/spec/lib/pwn/redaction_spec.rb +13 -10
- data/spec/lib/pwn/session_cleanup_concurrency_spec.rb +201 -0
- data/spec/lib/pwn/session_cleanup_default_spec.rb +165 -2
- data/spec/lib/pwn/session_cleanup_legacy_spec.rb +101 -0
- data/spec/lib/pwn/session_cleanup_payload_spec.rb +335 -2
- data/spec/lib/pwn/session_cleanup_recovery_spec.rb +389 -0
- data/spec/lib/pwn/sessions_spec.rb +112 -0
- data/spec/lib/pwn/setup_spec.rb +3 -1
- data/spec/support/gate_failure_formatter.rb +14 -0
- data/third_party/pwn_rdoc.jsonl +15 -1
- metadata +14 -7
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 41b180063b404ae6511a5df181eb969dcf2970b1ac128225369d6ec69afb30cc
|
|
4
|
+
data.tar.gz: 7936dc0f2c4552327650e74f3f0af790da4fbd353ca5d48cb651542d3f550acd
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 1184f82a572f136b6421f04059b5e05e79642e7f68337f376392c816aeb023b0bd0d74c15f0916c84078ad7188d255f0776ac04d2a87cbb31e9ee8fa59e6dc16
|
|
7
|
+
data.tar.gz: 851035353589425ab5ff5fdefb04c9c2a1f2c91fce7cb5d1e02067bd0eebb16905284bbf6cd7cde6373fa2ea9b7b1f6dbf531089ee9319988bd62ec7e83cdeb1
|
data/Gemfile
CHANGED
|
@@ -22,7 +22,7 @@ gem 'brakeman', '8.1.0'
|
|
|
22
22
|
gem 'bson', '5.2.0'
|
|
23
23
|
gem 'bundler', '>=4.0.23'
|
|
24
24
|
gem 'bundler-audit', '>=0.9.3'
|
|
25
|
-
gem 'bunny', '3.
|
|
25
|
+
gem 'bunny', '3.5.0'
|
|
26
26
|
gem 'colorize', '1.1.0'
|
|
27
27
|
gem 'credit_card_validations', '10.0.0'
|
|
28
28
|
gem 'curses', '1.7.0'
|
|
@@ -49,7 +49,7 @@ gem 'jwt', '3.3.0'
|
|
|
49
49
|
gem 'libusb', '0.8.0'
|
|
50
50
|
gem 'luhn', '3.0.0'
|
|
51
51
|
gem 'mail', '2.9.1'
|
|
52
|
-
gem 'mcp', '1.8.
|
|
52
|
+
gem 'mcp', '1.8.2'
|
|
53
53
|
gem 'meshtastic', '0.0.189'
|
|
54
54
|
gem 'metasm', '1.0.6'
|
|
55
55
|
gem 'mongo', '2.26.0'
|
|
@@ -94,7 +94,7 @@ gem 'selenium-webdriver', '4.51.0'
|
|
|
94
94
|
gem 'slack-ruby-client', '3.3.0'
|
|
95
95
|
gem 'socksify', '1.8.1'
|
|
96
96
|
gem 'spreadsheet', '1.3.5'
|
|
97
|
-
gem 'sqlite3', '2.10.
|
|
97
|
+
gem 'sqlite3', '2.10.2'
|
|
98
98
|
gem 'thin', '2.0.1'
|
|
99
99
|
gem 'tty-prompt', '0.23.1'
|
|
100
100
|
gem 'tty-spinner', '0.9.3'
|
data/Rakefile
CHANGED
|
@@ -5,12 +5,30 @@
|
|
|
5
5
|
exec('bundle', 'exec', 'rake', *ARGV) unless ENV['BUNDLE_BIN_PATH'] || ENV['RUBYOPT'].to_s.include?('bundler/setup')
|
|
6
6
|
|
|
7
7
|
require 'bundler/setup'
|
|
8
|
+
require 'English'
|
|
8
9
|
require 'bundler/gem_tasks'
|
|
9
10
|
require 'rspec/core/rake_task'
|
|
10
11
|
require 'rdoc/task'
|
|
11
12
|
require 'rubocop/rake_task'
|
|
12
13
|
|
|
13
|
-
RSpec
|
|
14
|
+
# RSpec's stock task maps a signalled child to exit 1 without naming the
|
|
15
|
+
# signal. Preserve that evidence when an interrupted run has no summary.
|
|
16
|
+
class DiagnosticSpecTask < RSpec::Core::RakeTask
|
|
17
|
+
def run_task(verbose)
|
|
18
|
+
super
|
|
19
|
+
rescue SystemExit
|
|
20
|
+
status = $CHILD_STATUS
|
|
21
|
+
if status
|
|
22
|
+
result = status.signaled? ? "signal=#{Signal.signame(status.termsig)}" : "exit=#{status.exitstatus}"
|
|
23
|
+
warn "RSpec child failed: #{result}"
|
|
24
|
+
end
|
|
25
|
+
raise
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
DiagnosticSpecTask.new(:spec) do |task|
|
|
30
|
+
task.rspec_opts = "#{task.rspec_opts} --require ./spec/support/gate_failure_formatter.rb --format progress --format GateFailureFormatter"
|
|
31
|
+
end
|
|
14
32
|
task :silence_rspec_filter_banner do
|
|
15
33
|
ENV['PWN_RAKE_SPEC'] = '1'
|
|
16
34
|
end
|
data/bin/pwn_setup
CHANGED
|
@@ -58,7 +58,7 @@ pwn_driver = PWN::Driver::Parser.new do |options|
|
|
|
58
58
|
end
|
|
59
59
|
|
|
60
60
|
options.on('--[no-]cron',
|
|
61
|
-
'<Optional - Enable (default) or disable
|
|
61
|
+
'<Optional - Enable (default) or disable the built-in pwn-ai cron worker; no OS scheduler changes>') do |o|
|
|
62
62
|
opts[:cron] = o
|
|
63
63
|
end
|
|
64
64
|
end
|
data/build_gem.sh
CHANGED
|
@@ -44,7 +44,7 @@ if [[ $old_ruby_version == $new_ruby_version ]]; then
|
|
|
44
44
|
export rvmsudo_secure_path=1
|
|
45
45
|
rvmsudo /bin/bash --login -c "cd ${pwn_root} && ./reinstall_gemset.sh"
|
|
46
46
|
cd /tmp && cd "$pwn_root"
|
|
47
|
-
rvmsudo /bin/bash --login -c "cd ${pwn_root} && bundle exec rake"
|
|
47
|
+
ruby "${pwn_root}/scripts/build_gate.rb" rvmsudo /bin/bash --login -c "cd ${pwn_root} && bundle exec rake"
|
|
48
48
|
rvmsudo /bin/bash --login -c "cd ${pwn_root} && bundle exec rake install"
|
|
49
49
|
rvmsudo /bin/bash --login -c "cd ${pwn_root} && bundle exec rake rerdoc"
|
|
50
50
|
echo "Invoking bundle-audit Gemfile Scanner..."
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# Historical migration backup retention
|
|
2
|
+
|
|
3
|
+
## Explicit legacy diagnostic and generated-copy expiry
|
|
4
|
+
|
|
5
|
+
`PWN::SessionCleanup.expire_legacy` is a separate, explicit maintenance entry point, defaulting to a preview. Apply requires `dry_run: false` and a **new external** `manifest:` path for one compressed metadata ledger; it does not archive payloads back into the managed home or run session/swarm retirement.
|
|
6
|
+
|
|
7
|
+
Only recognized expired closed request logs, the exact `learning.jsonl.bak-lean-YYYYMMDD` and `preferences.jsonl.bak-p15-YYYYMMDDhhmmss` recovery families, and verified duplicate BinaryParser triage caches qualify. Root recovery retains at least the latest three nonempty parseable copies per family, all literal reference pins, and copies younger than thirty days. Cache expiry requires byte-identical retained ArtifactRegistry evidence; absent inputs are not claimed reproducible. Decompiler stdout caches are not adopted by this policy.
|
|
8
|
+
|
|
9
|
+
The operation holds the canonical maintenance lock, rejects unsafe path/inode identities, checks retained references without exposing content, and checks process descriptors before classification and unlink. Existing producer leases must be available. Retained recovery/evidence proofs and candidate hashes are rechecked before unlink. An unreadable process inventory fails closed; do not bypass it when an older client is active. Coordinate any legacy writers before applying, and keep the learning producer lock during root recovery expiry. The external ledger records prepared versus completed state; it is not an undo archive. Existing immutable workers require a separately verified matched release before this entry point is deployed; it is not automatically enabled by importing the source.
|
|
10
|
+
|
|
11
|
+
## Migration generations
|
|
12
|
+
|
|
13
|
+
Migration backups are recovery generations, not an indefinite archive of every distinct historical byte. `PWN::Migrate.prune_backups` stays duplicate-only until an operator explicitly authorizes historical expiry. Its optional `expire_history: true` argument or private `~/.pwn/backup/.retention-policy.json` enables generation retention:
|
|
14
|
+
|
|
15
|
+
```json
|
|
16
|
+
{"version":1,"expire_history":true,"keep":3,"pins":[]}
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
`keep` cannot be less than three. `pins` contains exact timestamp generation directory names. Current structured evidence paths and prepared/interrupted or explicitly pinned maintenance records also retain their referenced generations. Quoted directory inventories and historical prose are not evidence dependencies. No logs, metrics, or historical learning versions are copied aside merely because their bytes are unique.
|
|
20
|
+
|
|
21
|
+
Before expiry, the newest retained point must cover every existing current migration-owned input path, and current encrypted configuration and decryptor bytes must match retained recovery copies. Every retained and expiring payload is hashed, current inputs and references are rechecked, symlinks are rejected, and deletion shares the snapshot producer's permanent `.retention.lock`. The ordinary maintenance transaction supplies its own maintenance lock and cooperative work admission. Manual backup-only calls must also coordinate live writers before apply. Default automatic batches consider at most ten older generations; exhausted admission defers expiry rather than weakening protection.
|
|
22
|
+
|
|
23
|
+
The single mode-0600 `.retention-manifest.json.gz` contains prepared/committed inventories and expiry names, not a second payload tree. Older unpinned versions are permanently expired: that manifest is an audit record, not an undo payload. Incomplete historical snapshots are never retrospectively certified complete. Their extant payloads can be verified for bounded recovery; the newly produced complete snapshot establishes current coverage. Restore by **copying only migration-owned paths**, not overlaying an entire old home, and never edit hardlinked backup bytes in place. Keep explicit pins for any recovery boundary needed during an active migration.
|
|
24
|
+
|
|
25
|
+
Scheduled retention is called by the managed v10 `SessionCleanup.maintain` helper through PWN's internal cron worker. No native scheduler or systemd installation is needed. Use `PWN::Migrate.prune_backups(dry_run: true)` for a strictly backup-only preview; do not invoke full session maintenance to test backup expiry. When no generation can expire, this preview avoids scanning evidence bodies and reports `evidence_scan: not_needed_no_expiry`; it is not a fresh payload-integrity audit.
|
|
26
|
+
|
|
27
|
+
An isolated maintenance release may coexist with older interactive clients: run the worker from the release's immutable `lib` load path, verify source/release/live-helper SHA-256 equality, and leave the active gem and clients untouched. The cron helper does not retrofit methods into those clients. A later normal application upgrade is separate from this scoped maintenance deployment.
|
data/documentation/Cron.md
CHANGED
|
@@ -2,17 +2,26 @@
|
|
|
2
2
|
|
|
3
3
|
`PWN::Cron` (`lib/pwn/cron.rb`) stores job definitions in
|
|
4
4
|
`~/.pwn/cron/jobs.yml`. `pwn setup` starts (or reuses) a background cron
|
|
5
|
-
worker
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
5
|
+
worker. Setup, migration and startup only seed internal job definitions;
|
|
6
|
+
they never install, start, repair or remove native scheduler units. After a
|
|
7
|
+
reboot, start/reuse the portable worker with `pwn setup --cron`; automatic
|
|
8
|
+
jobs require that worker, while manual commands do not. `--no-cron` disables
|
|
9
|
+
the internal worker without changing host services.
|
|
10
|
+
|
|
11
|
+
**Housekeeping must never use systemd or another native scheduler.**
|
|
12
|
+
The three enabled housekeeping defaults below use only this existing worker
|
|
13
|
+
or manual commands. `install_native_job` and each platform-specific job
|
|
14
|
+
installer explicitly skip housekeeping (`housekeeping_internal_only`),
|
|
15
|
+
including recognizable cleanup payloads under another name. Native worker
|
|
16
|
+
contexts exclude housekeeping from ticks and direct job execution.
|
|
17
|
+
|
|
18
|
+
Generic scheduler APIs remain for unrelated custom jobs: `install_crontab:
|
|
19
|
+
true` can still add their per-job system lines. They are not called by setup,
|
|
20
|
+
migration or default seeding. Existing housekeeping units/drop-ins are left
|
|
21
|
+
untouched and require explicit operator approval for removal; they are never
|
|
22
|
+
re-enabled automatically. See [Installation](Installation.md) for the legacy
|
|
23
|
+
service migration boundary. Do not create another worker or run cleanup just
|
|
24
|
+
to validate scheduling.
|
|
16
25
|
|
|
17
26
|

|
|
18
27
|
|
|
@@ -34,33 +43,53 @@ does not remove existing per-job lines.
|
|
|
34
43
|
| `cron_enable` / `cron_disable` | Toggle without deleting |
|
|
35
44
|
| `cron_remove` | Delete from `jobs.yml` (does **not** scrub crontab - `crontab -e` yourself) |
|
|
36
45
|
|
|
37
|
-
##
|
|
38
|
-
|
|
39
|
-
`
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
46
|
+
## Default housekeeping and explicit curriculum opt-in
|
|
47
|
+
|
|
48
|
+
`PWN::Cron.install_defaults` seeds **one** enabled job on a fresh install:
|
|
49
|
+
`session_cleanup`, daily at `45 3 * * *`, guarded until its first scheduled slot.
|
|
50
|
+
It calls `SessionCleanup.coordinate` through the existing internal worker:
|
|
51
|
+
|
|
52
|
+
1. Shared maintenance: preflight, evidence migration, protected-session cleanup,
|
|
53
|
+
and disposable storage maintenance.
|
|
54
|
+
2. Memory consolidation (`Learning.consolidate`, including Memory lean).
|
|
55
|
+
3. Learning outcome retention (`Learning.prune_outcomes!`).
|
|
56
|
+
4. Mistakes retention (`Mistakes.lean!`).
|
|
57
|
+
5. Policy trajectory retention (`Policy.lean!`).
|
|
58
|
+
|
|
59
|
+
The coordinator holds the permanent maintenance lock across these phases and
|
|
60
|
+
records their separate results. Blocked/incomplete maintenance skips retention;
|
|
61
|
+
dry-run does not consolidate memory or mutate stores. Store writer locks and
|
|
62
|
+
existing gold/recent/protected-entry policies remain in force. It does not call
|
|
63
|
+
`gc_stores!`, which would repeat memory consolidation and session cleanup.
|
|
64
|
+
|
|
65
|
+
Existing jobs are preserved verbatim, including disabled/custom schedules and
|
|
66
|
+
legacy curriculum aliases. Existing `learning_consolidate_nightly` and
|
|
67
|
+
`pwn_stores_lean_nightly` are **not** removed automatically. If either exists
|
|
68
|
+
when a missing session job is seeded, that job uses maintenance-only payloads
|
|
69
|
+
rather than duplicate retention. Consolidate those live schedules only after
|
|
70
|
+
all writers and the matched helper have been safely upgraded and tested.
|
|
71
|
+
|
|
72
|
+
Curriculum schedules are **not seeded at all**, including after deletion,
|
|
73
|
+
startup, or repeat migration. `CURRICULUM_JOBS` contains optional templates for
|
|
74
|
+
`curriculum_practice_nightly`, `curriculum_train_weekly`, and
|
|
75
|
+
`curriculum_offline_judge`. Explicitly create a selected template only when
|
|
76
|
+
wanted; templates default disabled:
|
|
50
77
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
- **offline_judge** - backfill outcome/process labels + plan calibration from PLAN `p(success)=` so `:failure_only` local introspect does not starve the corpus; also runs `Reward.warm_sentinel` so the reward-sentinel window can fill on local hosts
|
|
56
|
-
- **dedupe** - legacy alias `offline_judge_nightly` counts as the same job; `install_defaults` will not double-seed `30 3 * * *` and disables the alias when both exist
|
|
57
|
-
- **train** - export SFT + balanced DPO, LoRA-train `pwn-vN+1`, replay Mistakes.top, promote only on win. `dry_run: false` only with a trainer+GPU
|
|
58
|
-
- **consolidate** - memory GC (semantic merge + importance eviction) so the injected MEMORY block stays high-signal
|
|
78
|
+
```ruby
|
|
79
|
+
spec = PWN::Cron::CURRICULUM_JOBS.find { |job| job[:name] == 'curriculum_practice_nightly' }
|
|
80
|
+
PWN::Cron.create(spec.merge(enabled: false, sync_native: false))
|
|
81
|
+
```
|
|
59
82
|
|
|
60
|
-
|
|
83
|
+
Practice runs real tools, judge can spend provider tokens, and training needs
|
|
84
|
+
its own explicit configuration/approval. See [Reinforcement Learning](Reinforcement-Learning.md).
|
|
61
85
|
|
|
62
|
-
|
|
63
|
-
|
|
86
|
+
For schedule-only deletion without touching native schedulers, use
|
|
87
|
+
`PWN::Cron.remove(id: exact_id, sync_native: false)`. It preserves job logs and
|
|
88
|
+
other evidence. Registry writes use a permanent lock and atomic replacement;
|
|
89
|
+
completion updates merge into the current registry and never resurrect a deleted
|
|
90
|
+
job. Old workers without this protocol must drain safely before a registry edit;
|
|
91
|
+
never use a forced stop while a job is writing. A current worker reloads the
|
|
92
|
+
registry each tick; ordinary schedule changes need no restart.
|
|
64
93
|
|
|
65
94
|
## Example
|
|
66
95
|
|
|
@@ -20,7 +20,7 @@ hardware).
|
|
|
20
20
|
| `pwn --ai PROMPT` | `bin/pwn` | Headless one-shot agent (CI-friendly) |
|
|
21
21
|
| `pwn setup` | `lib/pwn/setup.rb` · `bin/pwn_setup` | Post-install doctor + capability provisioner + `--migrate` state doctor (also `pwn --setup[=PROFILE]`) |
|
|
22
22
|
| `bin/` (including `pwn-ai` and `pwn-capd`) | 56 files | Thin OptionParser wrappers plus the `pwn` REPL / one-shot agent |
|
|
23
|
-
| `PWN::Cron` | `lib/pwn/cron.rb` | Scheduled jobs + background worker (`pwn setup`). Fresh install seeds
|
|
23
|
+
| `PWN::Cron` | `lib/pwn/cron.rb` | Scheduled jobs + background worker (`pwn setup`). Fresh install seeds one `session_cleanup` housekeeping coordinator; curriculum schedules require explicit creation. Existing schedules are preserved |
|
|
24
24
|
|
|
25
25
|
## L2 - AI agent core (`lib/pwn/ai/agent/`)
|
|
26
26
|
|
|
@@ -1,5 +1,190 @@
|
|
|
1
1
|
# Installation
|
|
2
2
|
|
|
3
|
+
## Housekeeping deployment and diagnostics
|
|
4
|
+
|
|
5
|
+
The packaged `etc/default_cron/session_cleanup.rb` is the shared implementation
|
|
6
|
+
for `/maintenance run`, `/maintenance dry-run`, `/sessions cleanup`, and the
|
|
7
|
+
managed `session_cleanup` cron job. `/maintenance status` reports physical file
|
|
8
|
+
counts, bytes, configured retention, budgets, incomplete scans and safe error
|
|
9
|
+
classes. These are storage counts, not the session menu's pagination count.
|
|
10
|
+
Curses executes maintenance on its existing command worker, not its render/input
|
|
11
|
+
thread. Ctrl+C requests a safe boundary; committed work is not rolled back.
|
|
12
|
+
|
|
13
|
+
Upgrade the installed gem, run `pwn setup --migrate --fix`, then restart PWN
|
|
14
|
+
clients **and the existing cron worker**. Source-checkout edits alone do not
|
|
15
|
+
update installed endpoints. Schema 11 seeds a version-1 housekeeping policy and
|
|
16
|
+
refreshes the managed helper. Existing job IDs, schedules, disabled states,
|
|
17
|
+
custom Ruby payloads, custom unmarked helpers and policy edits are preserved.
|
|
18
|
+
A custom helper is not automatically upgraded. Fresh setup is idempotent and
|
|
19
|
+
registration never runs cleanup; the default job retains its next-slot guard.
|
|
20
|
+
Startup performs fixed-path checks and coalesces a daily background request in
|
|
21
|
+
`maintenance/background.json`. The existing cron worker consumes it through the
|
|
22
|
+
existing enabled job, never through a second worker. Disabled jobs stay disabled.
|
|
23
|
+
Do not apply maintenance while old untracked clients are running: unknown
|
|
24
|
+
legacy ownership blocks mutation rather than guessing which sessions are active.
|
|
25
|
+
|
|
26
|
+
Storage is organized into logical categories without relocating literal evidence
|
|
27
|
+
anchors:
|
|
28
|
+
|
|
29
|
+
| Category | Existing/new locations under `~/.pwn` | Retention contract |
|
|
30
|
+
|---|---|---|
|
|
31
|
+
| sessions | `sessions/` including support and requests | Eligible unsummarized, inactive, unreferenced transcripts only; physical deletion with backups |
|
|
32
|
+
| learning | `learning.jsonl`, `learning/`, `memory*.json`, `mistakes.json`, policy/curriculum/extrospection stores, finetune and preference records | Evidence migration retains exact verified snapshots; unknown records remain protected |
|
|
33
|
+
| artifacts | `artifacts/`, `exports/`, `findings.jsonl`, engagements, corpora and binary workspaces | Durable evidence, not disposable cache |
|
|
34
|
+
| state | `swarm/`, `cron/`, `agents.yml`, `jobs/`, `missions/`, `runs/`, metrics | Preserve continuation associations and active work |
|
|
35
|
+
| logs | `logs/` | Only explicitly registered disposable closed logs are eligible; unknown logs remain |
|
|
36
|
+
| cache | `cache/` | Only producer-registered, checksum-verified, inactive disposable files |
|
|
37
|
+
| tmp | `tmp/` | Same registration contract; never glob-delete arbitrary temp files |
|
|
38
|
+
| maintenance | `maintenance/`, legacy `session_cleanup_archive/` | Policy, permanent locks, metadata ledgers and recoverable quarantine |
|
|
39
|
+
|
|
40
|
+
`maintenance/policy.json` is non-secret JSON, version 1. `batch_size` defaults to
|
|
41
|
+
100; `scan_limit` to 100000. Cooperative admission defaults to `work_items: 100000`,
|
|
42
|
+
`work_seconds: 30` and `work_bytes: 67108864` for dependency and maintenance
|
|
43
|
+
payload reads, plus reserved disposable backup writes. Existing policy
|
|
44
|
+
files acquire these defaults in memory, without rewriting operator values.
|
|
45
|
+
`maintenance/layout.json` upgrades recognized inventory v1 to v2 separately from
|
|
46
|
+
policy; unknown inventory versions are refused. `retention_days` defaults to sessions 0, logs 30,
|
|
47
|
+
cache 7, tmp 1, maintenance 90. Null disables that category's expiry. Session
|
|
48
|
+
age is an additional floor, never an override of leases, provenance or evidence.
|
|
49
|
+
Learning/artifacts/state remain non-expiring: their policy slots are descriptive,
|
|
50
|
+
not authorization to erase evidence. `budget_bytes` enforces pressure eviction
|
|
51
|
+
for verified inactive disposable files (logs 256 MiB, cache 512 MiB, tmp 256 MiB,
|
|
52
|
+
maintenance 1 GiB), including before ordinary expiry. Null retention still
|
|
53
|
+
disables deletion. Budgets never evict protected evidence. Over-budget protected storage needs
|
|
54
|
+
operator review, not automatic deletion. Inventory scans are bounded and report
|
|
55
|
+
incompleteness rather than claiming a complete total. `bytes` counts logical file
|
|
56
|
+
bytes; `unique_bytes` counts each inode once per category. Directory discovery is
|
|
57
|
+
streamed for inventory and dependency trees. Work/time admission is cooperative
|
|
58
|
+
between phases and items, not a hard deadline: an admitted read, hash or atomic
|
|
59
|
+
commit completes. Exhausted/partial dependency indexes block deletion and report
|
|
60
|
+
`incomplete`/`interrupted`. Retrying recomputes dependencies; no timeout kills a writer.
|
|
61
|
+
Disposable cleanup reserves the complete payload transaction before its first
|
|
62
|
+
hash: one bounded read for preview, or five payload-size passes for verification,
|
|
63
|
+
backup read/write, backup verification and final source verification on apply.
|
|
64
|
+
Reads reject size changes and cannot follow unlimited concurrent growth. Quarantine,
|
|
65
|
+
legacy-backup inventory/compaction, atomic-temp recovery and transaction-status
|
|
66
|
+
reads also use byte admission. Policy/cursor metadata and durable ledger writes
|
|
67
|
+
are control overhead, not a hard total-filesystem-I/O quota; directory name
|
|
68
|
+
discovery is not a payload-byte allowance. An oversized item is retained, with
|
|
69
|
+
the overall maintenance run marked `incomplete`/`interrupted`; raise the explicit
|
|
70
|
+
work budget to process it. Once admitted, backup/commit finishes even if the
|
|
71
|
+
deadline expires mid-write.
|
|
72
|
+
|
|
73
|
+
Producer integration uses `PWN::SessionCleanup.register_disposable(path:,
|
|
74
|
+
category:, expires_at:)` after closing a reproducible file. Producers must hold
|
|
75
|
+
its permanent `<path>.lease` exclusively while writing, and readers hold it
|
|
76
|
+
shared throughout use. The registry records exact SHA-256, inode and byte count.
|
|
77
|
+
Changed, unknown, symlinked, active and invalid files are retained with reasons.
|
|
78
|
+
Disposable scans persist a lexical round-robin cursor in
|
|
79
|
+
`maintenance/disposable.cursor`; `scan_limit` bounds inspected manifests per run.
|
|
80
|
+
Completed/missing manifests remain for audit, including normal temporary-producer
|
|
81
|
+
unlinks, but cannot monopolize later scans. Active/changed entries are revisited
|
|
82
|
+
on the next circuit. The cursor is scheduling state only: cleanup never removes
|
|
83
|
+
or relocates registry entries, and rechecks registration bytes under the producer
|
|
84
|
+
lease so a concurrent renewal cannot authorize deletion using an old expiry.
|
|
85
|
+
Dry-run does not advance the cursor. A partial circuit reports `incomplete`.
|
|
86
|
+
Registration is not permission to classify evidence as disposable. Legacy caches
|
|
87
|
+
and logs are not implicitly registered. Transcript compaction/append instead use
|
|
88
|
+
a permanent transcript `.write.lock`; compaction temporary files are unique,
|
|
89
|
+
cleaned in ensure, fsynced and atomically replaced. Corrupt transcripts raise,
|
|
90
|
+
not silently compact into partial transcripts.
|
|
91
|
+
|
|
92
|
+
Ghidra decompile and BinaryParser triage now publish and read their canonical
|
|
93
|
+
caches under permanent leases. Ordinary `Log.append` diagnostics now go to
|
|
94
|
+
`logs/pwn.log`, rotating at 1,024,000 bytes into registered closed segments.
|
|
95
|
+
Debug streams hold a lifetime lease and register after request closure; active
|
|
96
|
+
streams cannot be evicted. Historical `/tmp/pwn.log` and learning logs are not
|
|
97
|
+
implicitly deleted. Cache writes and transcript compaction clean their own
|
|
98
|
+
temporary outputs on failure rather than leaving routine temp garbage.
|
|
99
|
+
|
|
100
|
+
Routine disposable cleanup writes exact private backups and prepared/committed
|
|
101
|
+
ledgers under `maintenance/quarantine/`. Only verified committed disposable
|
|
102
|
+
backups expire after the maintenance retention period or under budget pressure.
|
|
103
|
+
Interrupted `expiry_prepared` operations resume from verified remaining backups.
|
|
104
|
+
Broad operations also have prepared/committed/interrupted metadata ledgers under
|
|
105
|
+
`maintenance/runs/`, exposed by status. Prepared/corrupt ledgers
|
|
106
|
+
and unique evidence are never expired. `prune_legacy_backups` recognizes actual
|
|
107
|
+
committed session snapshots, dependency-removal backups, learning migration and
|
|
108
|
+
continuation-retirement ledgers. After retention or under maintenance pressure it
|
|
109
|
+
reclaims **redundant physical bytes** using checksum/readback-verified atomic
|
|
110
|
+
hardlink replacement. Recovery filenames remain intact; the last copy is never
|
|
111
|
+
removed. Unknown/uncommitted records are excluded. A cross-filesystem link failure
|
|
112
|
+
retains the original. Prepared/committed compaction journals are resumable.
|
|
113
|
+
|
|
114
|
+
`PWN::SessionCleanup.recovery_status(ledger: absolute_ledger_path)` identifies
|
|
115
|
+
supported actions. `recover(ledger:, action:, dry_run: true)` is explicit:
|
|
116
|
+
- `restore`: quarantine or legacy session copies, with no overwrite of different bytes;
|
|
117
|
+
- `rollback`: Learning's validated rollback or an unchanged continuation retirement;
|
|
118
|
+
- `rollback_dependencies`: exact targeted store rollback, after restoring source sessions;
|
|
119
|
+
- `resume`: re-evaluate broad maintenance, resume backup compaction, or re-evaluate
|
|
120
|
+
offline layout migration (apply requires `writers_stopped: true`).
|
|
121
|
+
Use `dry_run: false` only after reviewing the plan. Restoring a metadata ledger
|
|
122
|
+
never undoes data changes. Broad operations have independently committed children,
|
|
123
|
+
not a fictitious all-or-nothing rollback. Unknown schemas require manual review.
|
|
124
|
+
`reorganize` remains explicit and offline; this upgrade does not move existing paths.
|
|
125
|
+
|
|
126
|
+
### Housekeeping acceptance ledger (isolated fixtures)
|
|
127
|
+
|
|
128
|
+
`spec/lib/pwn/session_cleanup_recovery_spec.rb` exercises the actual maintenance
|
|
129
|
+
entry point, without live stores or providers:
|
|
130
|
+
|
|
131
|
+
| Requirement | Regression evidence | Boundary |
|
|
132
|
+
|---|---|---|
|
|
133
|
+
| Byte admission precedes disposable payload I/O | Expired 1 MiB cache with `work_bytes: 1024` remains intact; no payload read/hash or backup, run incomplete/interrupted | Cooperative allowance, not a hard filesystem-I/O cap |
|
|
134
|
+
| Repeated bounded scans progress | `scan_limit: 1`, two expired caches and a normally unlinked temporary producer; repeated runs delete both caches and retain all manifests | Registry pathname discovery still enumerates names; inspection is bounded |
|
|
135
|
+
| Active/mutated data remains protected | Held shared lease and changed payload survive while later eligible data is processed; released lease is revisited | Requires cooperating permanent producer leases |
|
|
136
|
+
| Re-registration is not lost | Renewing the same file between discovery and lease acquisition retains it; republished cache survives a later cursor circuit | Cursor never moves/deletes registry records |
|
|
137
|
+
| Admitted commit finishes safely | Deadline expires after backup publication; exact backup and committed child deletion survive, enclosing run is interrupted | Not all-or-nothing rollback |
|
|
138
|
+
| Related quarantine reads respect admission | Expired 1 MiB backup exceeds an 8 KiB work allowance and remains committed/unexpired | Legacy/atomic paths also admit reads; their existing integrity/recovery regressions remain enabled |
|
|
139
|
+
|
|
140
|
+
The completed isolated-HOME `bundle exec rake` gate passed 4,568 examples with
|
|
141
|
+
zero failures and inspected 1,161 files with no RuboCop offenses (exit 0).
|
|
142
|
+
Cancellation regressions distinguish preservation before deletion admission from
|
|
143
|
+
completion of an already-admitted commit; redaction covers the canonical log and
|
|
144
|
+
debug/progress/mirror/stderr/fallback sinks. These are fixture-based verification,
|
|
145
|
+
not live deployment acceptance: no live-store maintenance, installed-helper
|
|
146
|
+
deployment or provider run is implied.
|
|
147
|
+
|
|
148
|
+
### Temporary producer ownership audit
|
|
149
|
+
|
|
150
|
+
The audit covers `Dir.tmpdir`, `Tempfile`, `mktmpdir` and atomic replacement
|
|
151
|
+
call sites, not a filesystem filename sweep:
|
|
152
|
+
- Festival speech input now uses `SessionCleanup.temporary` in `~/.pwn/tmp`, with
|
|
153
|
+
a lifetime lease and exact registration. Concurrent calls have unique names;
|
|
154
|
+
normal completion unlinks the input and a crashed consumer leaves registered bytes.
|
|
155
|
+
- Ghidra/BinaryParser canonical cache publication and managed temporary inputs
|
|
156
|
+
journal atomic ownership (inode/device, expected hash, exact target) under
|
|
157
|
+
`maintenance/atomic`. `recover_atomic_temps` rejects active writers, changed or
|
|
158
|
+
partial bytes, missing journals and unknown paths. Routine maintenance applies
|
|
159
|
+
retention; explicit recovery can inspect an abandoned journal immediately.
|
|
160
|
+
- Sandbox/Detonate, GPS, RFID, OSINT bridge and GhidraHeadless work directories,
|
|
161
|
+
GDB stdin, and policy-evaluation/Solve verification trees are scoped block or
|
|
162
|
+
owner-cleaned resources. Old unregistered crash remnants are retained, not
|
|
163
|
+
inferred disposable from a prefix. GhidraHeadless's legacy/custom cache is not
|
|
164
|
+
silently enrolled as a canonical cache.
|
|
165
|
+
- Nmap/Nuclei scan output, proxy HAR, browser screenshots, APK output, Workspace
|
|
166
|
+
downloads, history spill and Burp diagnostics may be unique evidence. Their
|
|
167
|
+
existing literal paths remain; no age-based global `/tmp` cleanup is authorized.
|
|
168
|
+
- OAuth/vault/selection, learning/session/map and SDR image atomic outputs may be
|
|
169
|
+
only-copy state or evidence. Their own ensure/rollback protocols remain; unknown
|
|
170
|
+
remnants are not automatically discarded. Solve lock inodes are permanent.
|
|
171
|
+
This is an intentional ownership/evidence boundary, not a claim that every plugin
|
|
172
|
+
scratch file is automatically reclaimable.
|
|
173
|
+
|
|
174
|
+
Swarm registry retirement alone deliberately keeps its saved conversation.
|
|
175
|
+
The command route exposes `/swarm retire NAME --continuation` explicitly.
|
|
176
|
+
`PWN::AI::Agent::Swarm.retire(name:, swarm_id:, local: true, continuation: true)`
|
|
177
|
+
explicitly retires the inactive association with a backup and prepared/committed
|
|
178
|
+
ledger. It does not itself delete the transcript. A later persona request creates
|
|
179
|
+
a new session; active associations cannot be retired. Persona map writers and
|
|
180
|
+
cleanup share a permanent continuation lock.
|
|
181
|
+
|
|
182
|
+
Safe checks: `/maintenance status` and `/maintenance dry-run` do not call a
|
|
183
|
+
provider. Do not use `run` as a diagnostic on production data without reviewing
|
|
184
|
+
the preview. Permission and disk failures retain recoverable bytes and report
|
|
185
|
+
error classes; no blanket root-directory chmod is attempted.
|
|
186
|
+
|
|
187
|
+
|
|
3
188
|
PWN ships as a **single Ruby gem** whose runtime is 100 % `autoload`ed -
|
|
4
189
|
a plugin whose native extension or OS binary is missing costs nothing until
|
|
5
190
|
you actually touch that constant. That means the install is **two steps**:
|
|
@@ -82,7 +267,7 @@ External toolchain used by
|
|
|
82
267
|
skills/ drift 1 legacy flat file(s) PWN::Config → run --migrate --fix
|
|
83
268
|
...
|
|
84
269
|
|
|
85
|
-
cron worker ok
|
|
270
|
+
cron worker ok internal worker pid=...
|
|
86
271
|
|
|
87
272
|
31 / 36 capabilities usable · 5 degraded
|
|
88
273
|
|
|
@@ -196,9 +381,11 @@ predates the running gem (`PWN::Migrate.needed?`).
|
|
|
196
381
|
Schema `v1` also seeds `PWN::Cron.install_defaults` - the nightly
|
|
197
382
|
`curriculum_practice` and weekly `curriculum_train` self-improvement jobs
|
|
198
383
|
(see [Reinforcement Learning](Reinforcement-Learning.md)). `pwn setup`
|
|
199
|
-
also starts the background cron worker
|
|
200
|
-
|
|
201
|
-
|
|
384
|
+
also starts or reuses the single portable background cron worker. Setup,
|
|
385
|
+
migration and startup seeding never install, enable, repair or remove OS
|
|
386
|
+
scheduler units. Defaults live only in `~/.pwn/cron/jobs.yml`; the worker
|
|
387
|
+
must be running for automatic execution (start it again after reboot with
|
|
388
|
+
`pwn setup --cron`). Manual commands remain available without a worker. Live Policy files (`policy.json`,
|
|
202
389
|
`policy_traj.jsonl`) are created on first agent turn when `ai.agent.policy`
|
|
203
390
|
is on.
|
|
204
391
|
|
|
@@ -208,8 +395,40 @@ in the scheduler's local timezone. First launch also seeds it through
|
|
|
208
395
|
is installed to `~/.pwn/cron/session_cleanup.rb`; managed copies refresh on
|
|
209
396
|
subsequent launches/migrations, while unmarked custom helpers are preserved.
|
|
210
397
|
An existing job named `session_cleanup` keeps its ID, schedule, enabled state,
|
|
211
|
-
payload
|
|
212
|
-
|
|
398
|
+
payload. Legacy native scheduler files are left untouched for an explicit
|
|
399
|
+
operator-approved removal; they are never refreshed or re-enabled by setup.
|
|
400
|
+
New jobs defer cleanup until their first scheduled time, rather than deleting
|
|
401
|
+
during initial catch-up.
|
|
402
|
+
|
|
403
|
+
Fresh installs now seed only this job, with `SessionCleanup.coordinate`: shared
|
|
404
|
+
evidence-first maintenance, memory consolidation, then separate learning,
|
|
405
|
+
mistakes and policy retention phases. Existing split maintenance schedules are
|
|
406
|
+
preserved until explicitly consolidated after a safe matched-code deployment.
|
|
407
|
+
Curriculum schedules require explicit creation; startup and repeat migrations
|
|
408
|
+
never recreate deleted curriculum jobs. See [Cron](Cron.md) for opt-in templates
|
|
409
|
+
and schedule-only deletion. Already-running old binaries must be upgraded before
|
|
410
|
+
their startup/config-refresh hooks can be relied on to honor the new defaults.
|
|
411
|
+
|
|
412
|
+
**Housekeeping never uses systemd or any other native scheduler.**
|
|
413
|
+
`session_cleanup`, `learning_consolidate_nightly`, and `pwn_stores_lean_nightly`
|
|
414
|
+
run only through PWN's built-in cron worker or manual commands. Native job
|
|
415
|
+
installers explicitly skip them with `housekeeping_internal_only`; this also
|
|
416
|
+
covers renamed payloads that invoke `SessionCleanup` or `session_cleanup.rb`.
|
|
417
|
+
Generic scheduler APIs remain available for unrelated custom jobs. Native
|
|
418
|
+
worker launchers mark their execution context and exclude housekeeping;
|
|
419
|
+
legacy PWN systemd cgroups are also excluded. An inherited terminal
|
|
420
|
+
`INVOCATION_ID` alone is not a scheduler context.
|
|
421
|
+
|
|
422
|
+
Existing installations require an explicit operator decision before stopping
|
|
423
|
+
legacy services or processes. Inventory `pwn-cron-*.timer` and `.service`, map
|
|
424
|
+
job IDs to `jobs.yml`, preserve custom schedules, then (only with approval)
|
|
425
|
+
disable and stop the housekeeping timers/services and the legacy shared
|
|
426
|
+
worker service, archive their unit files/drop-ins, remove those exact files,
|
|
427
|
+
and run the user manager's daemon-reload. Do not remove unrelated custom job
|
|
428
|
+
units or change lingering globally. Coordinate old clients before deploying
|
|
429
|
+
the new code, retain `jobs.yml` unchanged, then start/reuse one internal
|
|
430
|
+
worker and verify its PID and guarded tick. Do not run cleanup to validate
|
|
431
|
+
this transition or re-enable legacy housekeeping units.
|
|
213
432
|
|
|
214
433
|
Cleanup calls a session named only when its persisted metadata has
|
|
215
434
|
`title_source: summary`, a nonblank title and summary, and a positive integer
|
|
@@ -321,6 +540,19 @@ Loop requests and title writers hold independent leases, and crashes release
|
|
|
321
540
|
descriptors automatically. Stale lock contents or reused PIDs confer no ownership.
|
|
322
541
|
Never unlink lock files: that would split the synchronization inode.
|
|
323
542
|
|
|
543
|
+
Routine maintenance supports multiple simultaneously running upgraded clients;
|
|
544
|
+
there is no blanket shutdown requirement. Evidence migration takes exclusive
|
|
545
|
+
nonblocking leases per source transcript through index publication, skips active
|
|
546
|
+
sources (`evidence_migration.deferred_active_sessions`), and revisits them on a
|
|
547
|
+
later run after their writers finish. Unrelated eligible sessions can still be
|
|
548
|
+
removed. A busy Learning writer returns
|
|
549
|
+
`evidence_migration.deferred: learning_writer_active` with `apply_blocked: true`
|
|
550
|
+
rather than waiting or interrupting the writer; retry on a later cron tick or
|
|
551
|
+
manual invocation. The permanent maintenance lock admits one applying owner;
|
|
552
|
+
a second receives `Session maintenance already running` immediately. Title
|
|
553
|
+
workers and durable job dependencies retain their existing protections.
|
|
554
|
+
This is routine maintenance, not the explicitly offline layout migration.
|
|
555
|
+
|
|
324
556
|
Old installed processes cannot be upgraded by refreshing the cron helper alone.
|
|
325
557
|
`legacy_owners` reports launchers without the held PID/start-identity capability
|
|
326
558
|
marker; `unknown_legacy_ownership` means their in-memory session IDs are unknown,
|
|
@@ -347,6 +579,15 @@ rake install # or: rvmsudo rake install (multi-user RVM)
|
|
|
347
579
|
pwn setup --profile full --yes # same provisioner, same data tables
|
|
348
580
|
```
|
|
349
581
|
|
|
582
|
+
The `upgrade_pwn.sh` → `build_gem.sh` validation gate still runs through
|
|
583
|
+
`rvmsudo` on multi-user RVM installations. Its merged stdout/stderr is streamed
|
|
584
|
+
and retained in the invoking user's `~/.pwn/logs/build-gate.log` (mode 0600,
|
|
585
|
+
last 1 MiB, replaced on the next gate). The path is printed before execution
|
|
586
|
+
and on failure; installation does not continue after a failed gate. `rake spec`
|
|
587
|
+
also prints each failure immediately and reports the RSpec child's exit status
|
|
588
|
+
or signal, so a later terminated example cannot hide earlier failure details.
|
|
589
|
+
A signal report is diagnostic evidence, not a claim about who sent the signal.
|
|
590
|
+
|
|
350
591
|
The legacy `./install.sh` / `packer/provisioners/pwn.sh` bash paths still
|
|
351
592
|
work, but they now simply delegate to `pwn setup` - the `case $os` package
|
|
352
593
|
lists have been consolidated into `PWN::Setup::NATIVE_GEMS` / `::TOOLCHAIN`.
|
|
@@ -26,7 +26,7 @@ external services and environment overrides can place evidence elsewhere.
|
|
|
26
26
|
| `extrospection/voice/*` | `Extrospection` | wav / txt | `rm -rf` | TTS/STT artifacts from `extro_voice` |
|
|
27
27
|
| `sessions/*.jsonl` | `PWN::Sessions` | JSON-per-line | `sessions_delete` | full transcript per pwn-ai run (with per-step `step_reward` from `Reward.prm`) |
|
|
28
28
|
| `open_goal.json` | `PWN::AI::Agent::OpenGoal` | JSON | delete the file | unfinished host-work request so `continue` / `resume` picks it up |
|
|
29
|
-
| `cron/jobs.yml` | `PWN::Cron` | YAML | `cron_remove` |
|
|
29
|
+
| `cron/jobs.yml` | `PWN::Cron` | YAML | `cron_remove` | Scheduled jobs; fresh install seeds only the `session_cleanup` housekeeping coordinator. Curriculum schedules require explicit creation and are not recreated after deletion |
|
|
30
30
|
| `cron/log/*.log` | `PWN::Cron` | text | rm | last_run output |
|
|
31
31
|
| `agents.yml` | `PWN::AI::Agent::Swarm` | YAML | edit / `agent_spawn` | persona registry |
|
|
32
32
|
| `swarm/<id>/bus.jsonl` | `Swarm` | JSON-per-line | rm -rf | append-only multi-agent chat |
|
|
@@ -359,14 +359,13 @@ This is the live control list. Track the outcomes, not source comments.
|
|
|
359
359
|
```ruby
|
|
360
360
|
# Seeded idempotently by PWN::Cron.install_defaults (pwn setup --migrate):
|
|
361
361
|
PWN::Cron.install_defaults
|
|
362
|
-
# ->
|
|
363
|
-
# -> curriculum_offline_judge 30 3 * * * Curriculum.offline_judge(since_hours: 24, limit: 40)
|
|
364
|
-
# -> curriculum_train_weekly 0 4 * * 0 Curriculum.train_and_gate(dry_run: true) # false only with trainer+GPU
|
|
365
|
-
# -> learning_consolidate_nightly 0 5 * * * Learning.consolidate
|
|
362
|
+
# -> session_cleanup 45 3 * * * SessionCleanup.coordinate(dry_run: false)
|
|
366
363
|
```
|
|
367
364
|
|
|
368
|
-
|
|
369
|
-
`
|
|
365
|
+
Curriculum practice, judge and training are explicit opt-ins, not seeded jobs.
|
|
366
|
+
Select a template from `PWN::Cron::CURRICULUM_JOBS` and explicitly create it only
|
|
367
|
+
when wanted (see [Cron](Cron.md)). Deletion survives repeated default installation
|
|
368
|
+
and migration. Existing jobs and legacy aliases remain unchanged.
|
|
370
369
|
`pwn setup` also starts the background cron worker so these YAML jobs fire
|
|
371
370
|
without a per-job crontab line.
|
|
372
371
|
|