@1aboveio/skills 1.0.18 → 1.0.20
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.codex-plugin/plugin.json +1 -1
- package/.codex-plugin/resources.json +151 -145
- package/package.json +4 -4
- package/skills/app-dev/airflow/SKILL.md +1 -1
- package/skills/app-dev/android-development/SKILL.md +1 -1
- package/skills/app-dev/app-debug/SKILL.md +1 -1
- package/skills/app-dev/better-auth/SKILL.md +1 -1
- package/skills/app-dev/data-catalog/SKILL.md +1 -1
- package/skills/app-dev/data-quality/SKILL.md +1 -1
- package/skills/app-dev/ios-foundation/SKILL.md +1 -1
- package/skills/app-dev/maestro/SKILL.md +1 -1
- package/skills/app-dev/monorepo/SKILL.md +1 -1
- package/skills/app-dev/nextjs-fullstack/SKILL.md +1 -1
- package/skills/app-dev/prisma-setup/SKILL.md +1 -1
- package/skills/app-dev/pyspark/SKILL.md +1 -1
- package/skills/app-dev/python-backend/SKILL.md +1 -1
- package/skills/app-dev/shadcn/SKILL.md +1 -1
- package/skills/app-dev/zod-v4/SKILL.md +1 -1
- package/skills/cicd/cloud-build/SKILL.md +1 -1
- package/skills/cicd/cloud-debug/SKILL.md +1 -1
- package/skills/cicd/cloud-deploy/SKILL.md +1 -1
- package/skills/cicd/docker/SKILL.md +1 -1
- package/skills/cicd/github-actions/SKILL.md +1 -1
- package/skills/cicd/github-flow/SKILL.md +1 -1
- package/skills/cicd/google-cloud/SKILL.md +1 -1
- package/skills/cicd/mergify/SKILL.md +1 -1
- package/skills/cicd/podman/SKILL.md +1 -1
- package/skills/content/article-review/SKILL.md +1 -1
- package/skills/content/article-to-image/SKILL.md +1 -1
- package/skills/content/flowchart-svg/SKILL.md +1 -1
- package/skills/content/image-gen/SKILL.md +1 -1
- package/skills/content/mermaid/SKILL.md +1 -1
- package/skills/content/wechat-article-writer/SKILL.md +1 -1
- package/skills/content/wechat-publisher/SKILL.md +1 -1
- package/skills/content/wechat-search/SKILL.md +1 -1
- package/skills/content/xai-search/SKILL.md +1 -1
- package/skills/core/distill-writing/SKILL.md +1 -1
- package/skills/core/doc-to-md/SKILL.md +1 -1
- package/skills/core/editorial-review/SKILL.md +1 -1
- package/skills/core/house-style/SKILL.md +1 -1
- package/skills/core/plain-language/SKILL.md +1 -1
- package/skills/core/polish-cn/SKILL.md +1 -1
- package/skills/core/reg-parser/SKILL.md +1 -1
- package/skills/core/terminology/SKILL.md +1 -1
- package/skills/core/write-doc/SKILL.md +1 -1
- package/skills/core/write-report/SKILL.md +1 -1
- package/skills/engineering/better-review/SKILL.md +5 -4
- package/skills/engineering/better-review/references/passes.md +15 -0
- package/skills/engineering/better-review/references/result.md +14 -0
- package/skills/engineering/e2e-test/SKILL.md +1 -1
- package/skills/engineering/engineering/SKILL.md +1 -1
- package/skills/engineering/engineering-runtime/SKILL.md +1 -1
- package/skills/engineering/engineering-runtime/coherence/workflow.json +13 -13
- package/skills/engineering/ensure-coverage/SKILL.md +1 -1
- package/skills/engineering/golden-path/SKILL.md +1 -1
- package/skills/engineering/harness-runtime/SKILL.md +1 -1
- package/skills/engineering/resolve-issues/SKILL.md +14 -9
- package/skills/engineering/resolve-issues/references/ci.md +3 -1
- package/skills/engineering/resolve-issues/references/delivery-progress.md +16 -0
- package/skills/engineering/resolve-issues/references/done.md +7 -0
- package/skills/engineering/resolve-issues/references/dropbox.md +31 -6
- package/skills/engineering/resolve-issues/references/expire.md +6 -3
- package/skills/engineering/resolve-issues/references/implementation.md +67 -6
- package/skills/engineering/resolve-issues/references/preflight.md +17 -3
- package/skills/engineering/resolve-issues/references/retention.md +162 -0
- package/skills/engineering/resolve-issues/references/review.md +42 -9
- package/skills/engineering/resolve-issues/references/task-plan.md +17 -2
- package/skills/engineering/resolve-issues/references/workspaces.md +40 -10
- package/skills/engineering/resolve-issues/scripts/daily-cleanup.mjs +169 -0
- package/skills/engineering/resolve-issues/scripts/dropbox.mjs +2 -1
- package/skills/engineering/resolve-issues/scripts/next-actions.mjs +79 -4
- package/skills/engineering/resolve-issues/scripts/resource-formats.mjs +625 -0
- package/skills/engineering/resolve-issues/scripts/retention-lock.mjs +548 -0
- package/skills/engineering/resolve-issues/scripts/retention-plans.mjs +57 -0
- package/skills/engineering/resolve-issues/scripts/retention.mjs +2314 -0
- package/skills/engineering/resolve-issues/scripts/review-verdict.mjs +63 -10
- package/skills/engineering/resolve-release/SKILL.md +1 -1
- package/skills/engineering/rush-issues/SKILL.md +1 -1
- package/skills/engineering/rush-release/SKILL.md +1 -1
- package/skills/engineering/test-quality/SKILL.md +1 -1
- package/skills/risk-compliance/compliance-audit/SKILL.md +1 -1
- package/skills/risk-compliance/contract-compare/SKILL.md +1 -1
- package/skills/risk-compliance/contract-review/SKILL.md +1 -1
- package/skills/risk-compliance/doc-authenticity/SKILL.md +1 -1
- package/skills/risk-compliance/fraud-detection/SKILL.md +1 -1
- package/skills/risk-compliance/payment-analysis/SKILL.md +1 -1
- package/skills/risk-compliance/portfolio-payment-analysis/SKILL.md +1 -1
- package/skills/risk-compliance/similar-domain/SKILL.md +1 -1
- package/skills/risk-compliance/tld-detection/SKILL.md +1 -1
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
# Daily resource retention
|
|
2
|
+
|
|
3
|
+
Keep run history and reclaim explicitly owned, reconstructable generated resources.
|
|
4
|
+
Enrollment belongs to the repository where `resolve-issues` or `rush-issues` is invoked;
|
|
5
|
+
it never discovers or enrolls sibling projects.
|
|
6
|
+
|
|
7
|
+
## Enroll at preflight
|
|
8
|
+
|
|
9
|
+
Use the persistent installed skill copy and canonical main checkout:
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
node <skill-base-dir>/scripts/daily-cleanup.mjs install --repo <repo-root>
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Repeat enrollment on each invocation to refresh the same job, including after a
|
|
16
|
+
skill or Node update. The installer prefers the stable PATH alias for its current
|
|
17
|
+
Node runtime. The skill directory must remain available; a temporary checkout or
|
|
18
|
+
unpack directory is not an installation.
|
|
19
|
+
|
|
20
|
+
macOS runs a user LaunchAgent every 12 hours and at load. Linux uses a persistent
|
|
21
|
+
user systemd daily timer with boot catch-up. The scheduler must be available; it
|
|
22
|
+
does not wake a powered-off machine. Report an unsupported host or failed enrollment
|
|
23
|
+
as "daily cleanup not enabled" and continue the issue workflow. A saved job file
|
|
24
|
+
alone does not prove that the job is loaded.
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
node <skill-base-dir>/scripts/daily-cleanup.mjs plan --repo <repo-root>
|
|
28
|
+
node <skill-base-dir>/scripts/daily-cleanup.mjs uninstall --repo <repo-root>
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Preserve records; register resources
|
|
32
|
+
|
|
33
|
+
Keep plans, source requirements, decisions, findings, results, review verdicts,
|
|
34
|
+
progress and retry history, checkpoints, Git metadata, unpublished work, required
|
|
35
|
+
evidence and credentials. A directory name or an old timestamp proves no authority.
|
|
36
|
+
Whole-workspace cleanup still follows [the delivered-unit lifecycle](workspaces.md#cleanup-after-delivery-before-finish).
|
|
37
|
+
|
|
38
|
+
The default age floors are seven full days for generated resources and 30 full
|
|
39
|
+
days for explicitly disposable raw artifacts. Recorded activity authorizes the age
|
|
40
|
+
calculation; newer filesystem writes can only postpone removal. Eligible targets
|
|
41
|
+
are ranked by measured allocation, largest first. Shared blocks, hardlinks and local
|
|
42
|
+
filesystem snapshots mean allocation removed is not necessarily free space gained.
|
|
43
|
+
Report the two measurements separately. Removing OS backup snapshots is a separate,
|
|
44
|
+
explicitly authorized operation, never part of this scheduler.
|
|
45
|
+
|
|
46
|
+
The manifest `<repo>/.resolve-issues/retention.json` binds each resource to its
|
|
47
|
+
run/unit, exact path and filesystem identity, activity, reconstruction recipe and
|
|
48
|
+
explicit registration. Register only paths already owned by the task plan. A
|
|
49
|
+
legacy path needs explicit ownership reconciliation first; inventory never adopts it.
|
|
50
|
+
|
|
51
|
+
Registration records inventory; it does not release an unfinished owner's resource
|
|
52
|
+
for deletion. Declare each generated resource's `retention` intent in the task plan
|
|
53
|
+
as described in [workspaces.md](workspaces.md#what-the-plan-records). Register the
|
|
54
|
+
ready declarations in one operation before worker admission:
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
node <skill-base-dir>/scripts/retention.mjs register-owned \
|
|
58
|
+
--repo <repo-root> --run <absolute-run> --unit <unit-id> \
|
|
59
|
+
--last-used-at <current-ISO-timestamp> --apply --json
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Without `--apply`, this previews registration and writes nothing. The command
|
|
63
|
+
considers only `lifecycle.resources` and their retention intent; legacy top-level
|
|
64
|
+
resource lists require explicit reconciliation. It never discovers neighboring
|
|
65
|
+
paths. Exit 0 means complete coverage, 1 means deferred or uncovered resources,
|
|
66
|
+
and 2 means the call was refused. Record its registered, deferred and uncovered results.
|
|
67
|
+
Refresh activity before reuse and repeat registration for newly populated resources
|
|
68
|
+
after their producing worker has settled. Keep that worker's resource report with
|
|
69
|
+
its result. A refusal means the resource remains unregistered; a loaded daily job
|
|
70
|
+
does not mean that every generated environment is covered.
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
node <skill-base-dir>/scripts/retention.mjs register \
|
|
74
|
+
--repo <repo-root> --run <absolute-run> --unit <unit-id> \
|
|
75
|
+
--path <absolute-generated-child> --kind generated \
|
|
76
|
+
--last-used-at <ISO-timestamp> --recipe '<reconstruction instructions>' --json
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
A resource containing credentials, private configuration, unique scripts or required
|
|
80
|
+
proof is retained. Malformed required-evidence declarations also retain candidates;
|
|
81
|
+
fix the ownership/evidence record rather than ignoring it. Registration and every
|
|
82
|
+
apply pass check these boundaries. Explicitly optional evidence is not a required pin.
|
|
83
|
+
|
|
84
|
+
For an unfinished task, use `--release-owner` only after explicit resource release
|
|
85
|
+
has been authorized and its reconstruction inputs remain available. It releases the
|
|
86
|
+
resource, not the task or source. Re-registering before reuse withdraws that release;
|
|
87
|
+
an existing pin remains pinned.
|
|
88
|
+
|
|
89
|
+
## Verify generated formats and rebuild inputs
|
|
90
|
+
|
|
91
|
+
Use `generic` for explicitly classified caches without a supported format. Its
|
|
92
|
+
conservative metadata and link checks remain in force. A supported generated format
|
|
93
|
+
can distinguish installed package source directories from the run's own source:
|
|
94
|
+
|
|
95
|
+
| Format | Positive proof | Retained reconstruction inputs |
|
|
96
|
+
| --- | --- | --- |
|
|
97
|
+
| `pnpm-install` | pnpm installation metadata and virtual store | `package.json`, `pnpm-lock.yaml` |
|
|
98
|
+
| `pnpm-store` | versioned content-addressed package store | `package.json`, `pnpm-lock.yaml` |
|
|
99
|
+
| `uv-venv` | virtual-environment configuration, interpreter and installed-package layout | `pyproject.toml`, `uv.lock` |
|
|
100
|
+
| `next-build` | Next.js build output markers | `package.json`, `pnpm-lock.yaml` |
|
|
101
|
+
|
|
102
|
+
Supply the format and actual input paths explicitly. The inputs must be canonical
|
|
103
|
+
regular files outside the disposable target. The registration records their identity
|
|
104
|
+
and content hashes; changed or uncertain inputs require reassessment. Format proof
|
|
105
|
+
does not exempt credentials, nested Git, run metadata, required evidence or ownership
|
|
106
|
+
checks. Links in a verified generated installation are references: removal unlinks
|
|
107
|
+
them and preserves their destinations.
|
|
108
|
+
|
|
109
|
+
```bash
|
|
110
|
+
node <skill-base-dir>/scripts/retention.mjs register \
|
|
111
|
+
--repo <repo-root> --run <absolute-run> --unit <unit-id> \
|
|
112
|
+
--path <workspace>/node_modules --kind generated --format pnpm-install \
|
|
113
|
+
--input <workspace>/package.json --input <workspace>/pnpm-lock.yaml \
|
|
114
|
+
--last-used-at <ISO-timestamp> --recipe 'pnpm install --frozen-lockfile' --json
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
## Check before reuse
|
|
118
|
+
|
|
119
|
+
Before admitting a worker that needs a registered resource, inspect its state:
|
|
120
|
+
|
|
121
|
+
```bash
|
|
122
|
+
node <skill-base-dir>/scripts/retention.mjs status \
|
|
123
|
+
--repo <repo-root> --run <absolute-run> --unit <unit-id> --json
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
Read `rebuildRequired`, the reason and retained recipe. A pin or protected credential
|
|
127
|
+
can prevent deletion without making a valid installation need reconstruction.
|
|
128
|
+
Readiness and cleanup eligibility are separate. A missing tree, changed
|
|
129
|
+
reconstruction inputs or a partial installation is not ready merely because its
|
|
130
|
+
parent directory exists. This includes environments left with protected credential
|
|
131
|
+
or configuration fragments after an explicitly authorized manual cleanup.
|
|
132
|
+
|
|
133
|
+
An unregistered or legacy resource has no readiness proof from this command. Read
|
|
134
|
+
its prior cleanup receipts and verify the repository's install/build contract before
|
|
135
|
+
reuse; do not infer readiness from an empty status result.
|
|
136
|
+
|
|
137
|
+
Reconstruct with the repository's retained lockfile and existing install/build
|
|
138
|
+
contract before continuing. Preserve protected files at their original paths; do
|
|
139
|
+
not run a clear/reset command that would discard them. Do not execute an arbitrary
|
|
140
|
+
recipe from an untrusted manifest. Verify the reconstructed format and then
|
|
141
|
+
re-register its new identity and current activity without `--release-owner`.
|
|
142
|
+
Until those steps pass, the dependent worker waits and the task stays unfinished.
|
|
143
|
+
|
|
144
|
+
## Preview, apply and recover
|
|
145
|
+
|
|
146
|
+
```bash
|
|
147
|
+
node <skill-base-dir>/scripts/retention.mjs plan --repo <repo-root> --json
|
|
148
|
+
node <skill-base-dir>/scripts/retention.mjs apply --repo <repo-root> --apply --json
|
|
149
|
+
node <skill-base-dir>/scripts/retention.mjs recover --repo <repo-root> --json
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
Apply locks retention and rechecks owner activity, claims, required evidence, source,
|
|
153
|
+
filesystem identity and process use before each removal. Receipts live outside the
|
|
154
|
+
target under `.resolve-issues/retention-receipts`. A partial removal is a failure;
|
|
155
|
+
preserve both the original failure and cleanup errors.
|
|
156
|
+
|
|
157
|
+
Recovery is read-only by default. `recover --apply` may clear only a lock whose
|
|
158
|
+
record proves its owner is dead on this host. Live, foreign, unreadable, legacy or
|
|
159
|
+
ambiguous locks stay. The daily apply path can make the same proven-dead-owner
|
|
160
|
+
recovery. It never breaks a lock by age. An interrupted receipt or quarantined tree
|
|
161
|
+
remains visible for reconciliation; releasing its dead lock does not declare its
|
|
162
|
+
cleanup complete or remove the quarantined data.
|
|
@@ -8,6 +8,13 @@ restate its rules in the brief beyond the two below.
|
|
|
8
8
|
|
|
9
9
|
## Brief
|
|
10
10
|
|
|
11
|
+
- Start a fresh reviewer conversation after each completed review round; the existing
|
|
12
|
+
scope script still chooses delta/full/skip, so freshness adds no review round or test
|
|
13
|
+
campaign. Give it retained records and code evidence as claims to verify, not an
|
|
14
|
+
implementer transcript. Launch it normally and keep its attempt reference as
|
|
15
|
+
`reviewSession`, separate from `implementationOwner`, so an unfinished same-HEAD round
|
|
16
|
+
can continue in that saved conversation; `retain_session` is optional protection, not
|
|
17
|
+
what makes it continuable. A round after a completed review is a fresh conversation.
|
|
11
18
|
- Reviewer model: a different id from the implementer's
|
|
12
19
|
([preflight.md](preflight.md#1-model-slots)). Launch the reviewer with the
|
|
13
20
|
plan's recorded reviewer fill — its `launch`, `instructions`, `selection` and
|
|
@@ -119,9 +126,23 @@ the unfinished work rather than repeating the whole assignment; preserve complet
|
|
|
119
126
|
valid passes. A wrong HEAD or invalid scope must be corrected, not carried as
|
|
120
127
|
completed proof.
|
|
121
128
|
|
|
129
|
+
On Pi, use `subagent_resume({id: reviewSession.runId, prompt: <unfinished work>})`
|
|
130
|
+
when that attempt completed as a process and its saved conversation is available;
|
|
131
|
+
update only its attempt reference, keeping this review's round, HEAD and counter.
|
|
132
|
+
A killed/lost attempt or an unavailable continuation follows replacement below. Once the
|
|
133
|
+
review record is complete, the next required review is a fresh conversation. Clear an
|
|
134
|
+
unpinned `reviewSession` directly. If it holds retention, release that protection before
|
|
135
|
+
clearing; on failure, save its exact reference and error in `retiredSessions` before clearing
|
|
136
|
+
so settlement can retry while the fresh review proceeds. Never use a reviewer as the
|
|
137
|
+
implementation owner.
|
|
138
|
+
|
|
122
139
|
If that reviewer cannot continue, state why replacement is necessary, which
|
|
123
140
|
retained evidence the replacement reads, and what it redoes before launching it.
|
|
124
|
-
|
|
141
|
+
A `retentionReleased` marker on the old reference is not a reason by itself: released
|
|
142
|
+
protection leaves the saved conversation in place, so try it again before replacing it.
|
|
143
|
+
While an old reference is still worth resuming or still needs release, keep it as an entry
|
|
144
|
+
in `retiredSessions` ([task-plan.md](task-plan.md#shape)) rather than dropping it; a later
|
|
145
|
+
release of the new session does not release the old conversation. The replacement reviews every pass with `tier: "full"`. Keep the round number,
|
|
125
146
|
`fixRound`, unit `attempt` and earlier round files; incompleteness neither resets
|
|
126
147
|
history nor counts as progress. Launch the replacement from the same recorded
|
|
127
148
|
reviewer fill, in the same structured launch arguments as [the brief](#brief)
|
|
@@ -221,10 +242,10 @@ the PR author; `github-reviews.mjs` reads them and decides what blocks.
|
|
|
221
242
|
|
|
222
243
|
- **P0/P1** block (`better-review`'s severity table defines them): fix on the
|
|
223
244
|
unit branch, validate, review again.
|
|
224
|
-
- **P2** never blocks
|
|
225
|
-
|
|
226
|
-
[
|
|
227
|
-
|
|
245
|
+
- **P2** never blocks or starts a blocking-fix round by severity alone. Route each
|
|
246
|
+
entry by the [dropbox questions](dropbox.md#route-every-problem); explicitly selected
|
|
247
|
+
[supporting P2 work](dropbox.md#supporting-p2-work) may be fixed in the unit.
|
|
248
|
+
Keep unrelated P2 in the dropbox; preserve the severity and review verdict.
|
|
228
249
|
- **P3** is skipped: no dropbox entry, no issue, no round. A P3 that still
|
|
229
250
|
reaches the result file (a posted result, an older reviewer) is ignored.
|
|
230
251
|
- Dispose of every P0-P2 finding: `resolved` on the reviewed HEAD, or `deferred`
|
|
@@ -232,13 +253,24 @@ the PR author; `github-reviews.mjs` reads them and decides what blocks.
|
|
|
232
253
|
|
|
233
254
|
## Fix loop
|
|
234
255
|
|
|
235
|
-
Give the
|
|
256
|
+
Give the [implementation owner](implementation.md#implementation-owner) the `fixBrief`
|
|
257
|
+
and act on its `action`. Keep the generated `reproducers` intact: their source rounds,
|
|
258
|
+
HEADs and original triggers identify earlier witnesses, not passing results on this
|
|
259
|
+
candidate. Read the retained cases and run against the candidate workspace at their
|
|
260
|
+
relative `cwd`; preserve their input/state and expected contract when adapting a checkout
|
|
261
|
+
path. `reproducerGaps` identifies malformed historical metadata without supplying a runnable
|
|
262
|
+
command: inspect the original record, establish a valid candidate-local case, and report any
|
|
263
|
+
remaining gap without rewriting history. Findings without an executable witness retain their
|
|
264
|
+
existing requirement evidence.
|
|
265
|
+
Add only supporting P2
|
|
266
|
+
explicitly selected under [dropbox routing](dropbox.md#supporting-p2-work), with the
|
|
267
|
+
recorded criterion and reason; the generated blocker list remains P0/P1 only.
|
|
236
268
|
|
|
237
269
|
| `action` | When | Fix worker |
|
|
238
270
|
|---|---|---|
|
|
239
271
|
| `fix` | first blocking round | Make the smallest complete fix for each finding's `rule` and close every applicable row in its `paths`; do not require centralization. |
|
|
240
272
|
| `test-first` | a P0/P1 survived the last fix | On the escalation model, for each `recurring` finding, before changing code: name the broken rule and its concrete open paths, reuse sufficient tests and add focused cases for missing proof, and see the open cases fail. Fix the rule until the applicable cases pass. A table-driven test may cover several paths; no new architecture is required. |
|
|
241
|
-
| `one-by-one` | the last fix introduced new P0/P1 |
|
|
273
|
+
| `one-by-one` | the last fix introduced new P0/P1 | The implementation owner fixes findings sequentially in its continued session, each with the smallest change that closes every row of its `paths` at the rule's owner. |
|
|
242
274
|
|
|
243
275
|
**Fix the broken rule on every applicable path, without requiring a refactor.**
|
|
244
276
|
A shared owner is useful when it makes the fix smaller and clearer; it is not a
|
|
@@ -251,8 +283,9 @@ different-person sign-in cases without inventing unrelated event combinations.
|
|
|
251
283
|
Have each fix worker run the checks its change affects and return the
|
|
252
284
|
commands and results; push, and let CI run. Then pick the new HEAD's
|
|
253
285
|
[tier](#tier-then-verdict): a fix is delta or full, never skip, so spawn the
|
|
254
|
-
next round's reviewer with the previous result file and the `fixBrief
|
|
255
|
-
|
|
286
|
+
next round's fresh reviewer with the previous result file and the `fixBrief`, naming any
|
|
287
|
+
supporting P2 changed in that patch. Deferred P2 return as findings only if now rated
|
|
288
|
+
P1; evaluate changed supporting work as part of the fix diff. Record `reviewedHead` only on a `pass` verdict. A later
|
|
256
289
|
commit, a CI fix included, needs its own `pass`: a [carry](#carry-after-a-pass) when this round passed.
|
|
257
290
|
|
|
258
291
|
A `decide` verdict pauses the fix loop on a question: a spec finding came back after a
|
|
@@ -92,6 +92,9 @@ Fleet `status`: `pending`, `in-flight`, `succeeded`, `failed`,
|
|
|
92
92
|
| `attempt`, `retries`, `escalations` | Attempt number; cycle counts; escalation-model attempts |
|
|
93
93
|
| `worker`, `workerSince`, `clock` | Slot identifier of any worker running for the unit (implementer, reviewer, fix), `null` when none; when that worker started (ISO time, for [stuck-worker checks](expire.md#waits)); attempt and cumulative wall time |
|
|
94
94
|
| `workerKind` | `implement`, `fix`, or `review`, set with `worker`; the [live-capacity scheduler](implementation.md#live-capacity) (`next-actions.mjs`) prints `worker-kind` for any other kind or none. An infrastructure diagnosis or a test-first worker is a `fix` worker. The explorer is the `explore` fleet row's worker, never a unit's; a merge-queue watcher is not a worker |
|
|
95
|
+
| `implementationOwner` | Optional saved implementation conversation: `{runId, sessionId?, cwd?, kind?, retentionReleased?}`. `runId` is its latest attempt; `kind` is `implement` (default) or `fix`, never `review`; `cwd`, when recorded, matches `workspace`. Save spawn/resume receipts here, independently of `worker`; a parked owner occupies no slot. The scheduler emits `owner-resume` for a usable owner or an explicit `ownerUnavailable` reason for replacement; `retentionReleased: true` only records that optional cleanup protection lapsed, so it never makes the owner unusable and the saved conversation stays the resume target while the harness holds it. Old plans remain readable. See [ownership](implementation.md#implementation-owner). |
|
|
96
|
+
| `reviewSession` | Optional `{runId, sessionId?, head, round, retentionReleased?}` for the current unfinished review. Keep it only for same-HEAD/same-scope continuation, and clear it when that round completes so the next completed-review round starts fresh. A `retentionReleased` marker is history, not a reason to replace: try the same round's saved conversation while it exists. It is never an implementation owner or a slot reservation. |
|
|
97
|
+
| `retiredSessions` | Optional references kept beside a replaced implementation owner or unfinished reviewer: `{runId, sessionId?, kind, retentionReleased?, reason?}`. Only for a conversation still worth resuming or still holding protection that must be released later; an ordinary resume or replacement needs no entry here, and a later release of the new session does not release an older conversation. |
|
|
95
98
|
| `estimatedLines` | Preflight assessment estimate |
|
|
96
99
|
| `sizeCheck` | Measures, `verdict` (the size check's per-unit verdict; `stop` means held), scope and proof verdicts, split or verification path; `runWhole: true` once a recommendation to run a held unit whole is accepted |
|
|
97
100
|
| `note` | SHA, expiry reason code, failure and response, or blocker; for active debugging, the immediate acceptance checkpoint, baseline/result link and next distinguishing test ([delivery progress](../SKILL.md#delivery-progress)) |
|
|
@@ -142,7 +145,10 @@ what a later launch of the slot repeats; a pair of model ids is not.
|
|
|
142
145
|
(`open`, `resolved`, `deferred`), `resolvedHead`, `dropboxId` or `followUpUrl`. Copy them
|
|
143
146
|
from `review-verdict.mjs`: on `fix` its `fix` list as `open`; on `pass` its
|
|
144
147
|
`deferred` list as `deferred` and every open P0/P1 as `resolved`. Never defer
|
|
145
|
-
a P0/P1.
|
|
148
|
+
a P0/P1. For selected [supporting P2 work](dropbox.md#supporting-p2-work), record the
|
|
149
|
+
selection in `decisions`; after verification, mark its plan finding `resolved` with
|
|
150
|
+
`resolvedHead` and put the check/result in the unit's existing note/result. Preserve
|
|
151
|
+
the original review and retire its pending dropbox entry as documented there.
|
|
146
152
|
|
|
147
153
|
`decisions` entry: id, timestamp, the human's words, what it changes,
|
|
148
154
|
`supersedes` when it replaces one, and `answers`: the run-relative file of each
|
|
@@ -153,7 +159,16 @@ recommendation's reason; its human's words are the invocation.
|
|
|
153
159
|
|
|
154
160
|
### Worker selection on resume
|
|
155
161
|
|
|
156
|
-
|
|
162
|
+
Distinguish resuming the workflow from continuing one saved conversation. For an ordinary
|
|
163
|
+
same-conversation `subagent_resume`, pass its attempt ID and new feedback, omitting role,
|
|
164
|
+
model and effort so the harness preserves its recorded recipe. The resume tool's schema
|
|
165
|
+
may differ from spawn: do not replay a role selector it cannot accept. A required changed
|
|
166
|
+
selection can continue only through controls both explicitly authorized and advertised by
|
|
167
|
+
that API; otherwise record why replacement is needed and launch fresh from the required
|
|
168
|
+
fill with its retained evidence. A resolved role default alone never authorizes a model pin.
|
|
169
|
+
Keep the original model/effort as run evidence and preserve all review/progress counters.
|
|
170
|
+
|
|
171
|
+
**A fresh or replacement launch repeats the saved fill, never a reconstruction of it.** On Pi the
|
|
157
172
|
launch authority stays the live role resolver ([shared-modules.md](shared-modules.md#model-slots)):
|
|
158
173
|
its `launch` is `{ role }` unless an explicit task override or this host's own
|
|
159
174
|
preference asked for a model or effort, and a resolved role default is never
|
|
@@ -17,7 +17,9 @@ checkout) and `targetBranch`. A unit already records `workspace`, `branch` and
|
|
|
17
17
|
"baseSha": "<exact SHA>", "identity": "<returned by the helper>" },
|
|
18
18
|
"resources": [ { "role": "review|scratch|tools", "kind": "directory|clone|worktree",
|
|
19
19
|
"path": "<absolute>", "disposable": true,
|
|
20
|
-
"state": "preparing|ready", "identity": "<returned by the helper>"
|
|
20
|
+
"state": "preparing|ready", "identity": "<returned by the helper>",
|
|
21
|
+
"retention": { "kind": "generated", "format": "generic|pnpm-install|pnpm-store|uv-venv|next-build",
|
|
22
|
+
"recipe": "<retained rebuild command>", "inputs": ["<absolute source input>"] } } ],
|
|
21
23
|
"cleanup": { "state": "pending|applying|complete|retained",
|
|
22
24
|
"headSha": "<the unit's headSha>", "reason": "<retained only>" } }
|
|
23
25
|
```
|
|
@@ -75,11 +77,31 @@ Do not admit another worker while that owner's cleanup is `applying`; the
|
|
|
75
77
|
3. Persist the returned `nextLifecycle` on the unit **before** spawning the
|
|
76
78
|
worker, and give the worker that workspace path and only the resources it
|
|
77
79
|
needs. Resources are recorded before they are used.
|
|
78
|
-
4.
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
80
|
+
4. Each disposable generated resource carries its explicit `retention` intent:
|
|
81
|
+
kind, supported format, reconstruction recipe and retained source input paths
|
|
82
|
+
([retention.md](retention.md#verify-generated-formats-and-rebuild-inputs)).
|
|
83
|
+
After persisting the ready lifecycle and before worker admission, run:
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
node <skill-base-dir>/scripts/retention.mjs register-owned \
|
|
87
|
+
--repo <repo-root> --run <run> --unit <unit> \
|
|
88
|
+
--last-used-at <current-ISO-timestamp> --apply --json
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
The command considers only declared resources. Record its registered, deferred
|
|
92
|
+
and uncovered results; report a refusal rather than claiming full daily coverage.
|
|
93
|
+
An empty typed installation can be deferred while its producing worker installs
|
|
94
|
+
it. After that worker settles and its handle is cleared, repeat registration
|
|
95
|
+
for the populated resource. Registration grants no early release of unfinished
|
|
96
|
+
work. Whole workspaces, credential directories, unique scripts and required
|
|
97
|
+
evidence stay outside disposable declarations.
|
|
98
|
+
5. Before reusing a registered resource, run the read-only
|
|
99
|
+
[rebuild status check](retention.md#check-before-reuse). Rebuild a missing or
|
|
100
|
+
partial installation with the retained repository contract before admitting its
|
|
101
|
+
dependent worker; directory existence is not readiness. Refresh registration and
|
|
102
|
+
activity without releasing the owner after reconstruction. Retries, fix rounds
|
|
103
|
+
and review rounds reuse the same workspace and review clone. A later prepare
|
|
104
|
+
verifies identity, branch and base; it does not reset or replace them.
|
|
83
105
|
|
|
84
106
|
`kind`: a linked `worktree` where a plain checkout is enough; an independent
|
|
85
107
|
`clone` when the first Git-mutating worker runs in a sandbox that cannot write a
|
|
@@ -126,8 +148,10 @@ It removes nothing else: no ignored-file stripping, no pruning, and no
|
|
|
126
148
|
answer retains everything, whole. Keep, never clean: a blocked, cancelled,
|
|
127
149
|
pending, or supervised merge-ready unit's environment, whatever its timestamp; a
|
|
128
150
|
tree a worker may still use; a tree whose ownership or delivery cannot be proved.
|
|
129
|
-
|
|
130
|
-
`*-workspaces` directory is never adopted or deleted.
|
|
151
|
+
Whole-workspace cleanup never runs by age, idle time, or a name pattern, and a legacy
|
|
152
|
+
`*-workspaces` directory is never adopted or deleted. Separate [resource retention](retention.md)
|
|
153
|
+
uses explicit registrations and age thresholds for reconstructable children while keeping
|
|
154
|
+
the workspace, Git history and progress records.
|
|
131
155
|
|
|
132
156
|
## Evidence and handoff
|
|
133
157
|
|
|
@@ -149,8 +173,14 @@ On startup, before scheduling, read only:
|
|
|
149
173
|
node <skill-base-dir>/scripts/cleanup-runs.mjs recover --repo <repo-root> --json
|
|
150
174
|
```
|
|
151
175
|
|
|
152
|
-
|
|
153
|
-
|
|
176
|
+
Also inspect resource-retention recovery before admission:
|
|
177
|
+
|
|
178
|
+
```bash
|
|
179
|
+
node <skill-base-dir>/scripts/retention.mjs recover --repo <repo-root> --json
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
Workspace recovery lists unfinished recorded operations — a `preparing` workspace,
|
|
183
|
+
an `applying` cleanup, a lock, an orphaned worktree — in every run regardless of age. It
|
|
154
184
|
activates no workflow and cleans nothing by itself: continue the plan, then
|
|
155
185
|
prepare or clean what it names and persist the result.
|
|
156
186
|
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Enroll only the explicitly named repository; no project-directory discovery.
|
|
3
|
+
import { createHash } from 'node:crypto'
|
|
4
|
+
import { execFileSync, spawnSync } from 'node:child_process'
|
|
5
|
+
import { existsSync, lstatSync, mkdirSync, readFileSync, realpathSync, rmSync, writeFileSync } from 'node:fs'
|
|
6
|
+
import { homedir } from 'node:os'
|
|
7
|
+
import { delimiter, dirname, isAbsolute, join, resolve } from 'node:path'
|
|
8
|
+
import { fileURLToPath } from 'node:url'
|
|
9
|
+
import { isMainModule } from '../../engineering-runtime/scripts/main-module.mjs'
|
|
10
|
+
|
|
11
|
+
const SCRIPT = fileURLToPath(import.meta.url)
|
|
12
|
+
const RETENTION = join(dirname(SCRIPT), 'retention.mjs')
|
|
13
|
+
const PATH = '/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin'
|
|
14
|
+
|
|
15
|
+
function days(value) {
|
|
16
|
+
const number = Number(value)
|
|
17
|
+
if (!Number.isSafeInteger(number) || number < 1) throw new Error('--older-than-days must be a positive integer')
|
|
18
|
+
return number
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function escapeXml(value) {
|
|
22
|
+
return String(value).replace(/[&<>"']/g, char => ({ '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' })[char])
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function systemdArgument(value) {
|
|
26
|
+
if (/[\r\n\0]/.test(value)) throw new Error('Scheduler paths cannot contain line breaks or NUL')
|
|
27
|
+
return `"${String(value).replace(/\\/g, '\\\\').replace(/"/g, '\\"').replace(/%/g, '%%').replace(/\$/g, '$$')}"`
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// Prefer the PATH alias for this exact runtime. Package managers can remove a
|
|
31
|
+
// versioned Cellar directory while keeping their stable opt/bin alias current.
|
|
32
|
+
export function durableNodePath(executable = process.execPath, searchPath = process.env.PATH ?? '') {
|
|
33
|
+
let identity
|
|
34
|
+
try { identity = realpathSync(executable) } catch { return executable }
|
|
35
|
+
for (const directory of searchPath.split(delimiter)) {
|
|
36
|
+
if (!isAbsolute(directory)) continue
|
|
37
|
+
const candidate = join(directory, 'node')
|
|
38
|
+
try { if (realpathSync(candidate) === identity) return candidate } catch { /* not this runtime */ }
|
|
39
|
+
}
|
|
40
|
+
return executable
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function schedulePlan({ repo, olderThanDays = 30, platform = process.platform,
|
|
44
|
+
home = homedir(), node = durableNodePath(), script = SCRIPT } = {}) {
|
|
45
|
+
if (!isAbsolute(repo ?? '')) throw new Error('--repo must be an absolute repository path')
|
|
46
|
+
const canonical = realpathSync(repo)
|
|
47
|
+
if (!existsSync(join(canonical, '.git'))) throw new Error('Daily cleanup requires a Git repository root')
|
|
48
|
+
if (!isAbsolute(home) || !isAbsolute(node) || !isAbsolute(script)) throw new Error('Scheduler paths must be absolute')
|
|
49
|
+
const retention = days(olderThanDays)
|
|
50
|
+
const id = `io.1above.resolve-issues.cleanup.${createHash('sha256').update(canonical).digest('hex').slice(0, 20)}`
|
|
51
|
+
const args = [node, script, 'run', '--repo', canonical, '--older-than-days', String(retention), '--apply']
|
|
52
|
+
if (args.some(value => /[\r\n\0]/.test(value))) throw new Error('Scheduler paths cannot contain line breaks or NUL')
|
|
53
|
+
if (platform === 'darwin') {
|
|
54
|
+
const logs = join(home, 'Library', 'Logs', 'resolve-issues')
|
|
55
|
+
const file = join(home, 'Library', 'LaunchAgents', `${id}.plist`)
|
|
56
|
+
const content = `<?xml version="1.0" encoding="UTF-8"?>\n<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">\n<plist version="1.0"><dict>\n<key>Label</key><string>${id}</string>\n<key>ProgramArguments</key><array>${args.map(arg => `<string>${escapeXml(arg)}</string>`).join('')}</array>\n<key>RunAtLoad</key><true/>\n<key>StartInterval</key><integer>43200</integer>\n<key>EnvironmentVariables</key><dict><key>PATH</key><string>${PATH}</string></dict>\n<key>StandardOutPath</key><string>${escapeXml(join(logs, `${id}.log`))}</string>\n<key>StandardErrorPath</key><string>${escapeXml(join(logs, `${id}.err`))}</string>\n</dict></plist>\n`
|
|
57
|
+
return { repo: canonical, id, retentionDays: retention, platform, files: [{ path: file, content }], directories: [logs], args }
|
|
58
|
+
}
|
|
59
|
+
if (platform === 'linux') {
|
|
60
|
+
const root = join(home, '.config', 'systemd', 'user')
|
|
61
|
+
const service = `[Unit]\nDescription=Reclaim declared disposable resolve-issues resources for ${id}\n\n[Service]\nType=oneshot\nEnvironment=PATH=${PATH}\nExecStart=${args.map(systemdArgument).join(' ')}\nTimeoutStartSec=30min\n`
|
|
62
|
+
const timer = `[Unit]\nDescription=Daily tiered resource retention for ${id}\n\n[Timer]\nOnCalendar=daily\nOnBootSec=5min\nPersistent=true\nUnit=${id}.service\n\n[Install]\nWantedBy=timers.target\n`
|
|
63
|
+
return { repo: canonical, id, retentionDays: retention, platform,
|
|
64
|
+
files: [{ path: join(root, `${id}.service`), content: service }, { path: join(root, `${id}.timer`), content: timer }], directories: [], args }
|
|
65
|
+
}
|
|
66
|
+
throw new Error(`Daily cleanup scheduling is unsupported on ${platform}; no job was installed`)
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const defaultRun = (command, args) => execFileSync(command, args, { encoding: 'utf8', timeout: 30000, stdio: ['ignore', 'pipe', 'pipe'] })
|
|
70
|
+
|
|
71
|
+
export function installSchedule(options = {}, { run = defaultRun, uid = process.getuid?.() } = {}) {
|
|
72
|
+
const plan = schedulePlan(options)
|
|
73
|
+
for (const file of plan.files) {
|
|
74
|
+
let current = file.path
|
|
75
|
+
while (current !== dirname(current)) {
|
|
76
|
+
try {
|
|
77
|
+
if (lstatSync(current).isSymbolicLink()) throw new Error(`Scheduler path is a symbolic link: ${current}`)
|
|
78
|
+
} catch (error) { if (error.code !== 'ENOENT') throw error }
|
|
79
|
+
current = dirname(current)
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
const domain = `gui/${uid}`
|
|
83
|
+
const same = plan.files.every(file => existsSync(file.path) && readFileSync(file.path, 'utf8') === file.content)
|
|
84
|
+
if (same) {
|
|
85
|
+
try {
|
|
86
|
+
if (plan.platform === 'darwin') run('launchctl', ['print', `${domain}/${plan.id}`])
|
|
87
|
+
else {
|
|
88
|
+
run('systemctl', ['--user', 'is-enabled', `${plan.id}.timer`])
|
|
89
|
+
run('systemctl', ['--user', 'is-active', `${plan.id}.timer`])
|
|
90
|
+
}
|
|
91
|
+
return { ok: true, action: 'unchanged', ...plan }
|
|
92
|
+
} catch { /* A saved job is not necessarily loaded. */ }
|
|
93
|
+
}
|
|
94
|
+
// Preflight the user scheduler before saving any files.
|
|
95
|
+
if (plan.platform === 'darwin') run('launchctl', ['print', domain])
|
|
96
|
+
else run('systemctl', ['--user', 'show-environment'])
|
|
97
|
+
for (const path of plan.directories) mkdirSync(path, { recursive: true })
|
|
98
|
+
if (plan.platform === 'darwin') {
|
|
99
|
+
let loaded = false
|
|
100
|
+
try { run('launchctl', ['print', `${domain}/${plan.id}`]); loaded = true } catch { /* absent */ }
|
|
101
|
+
if (loaded) run('launchctl', ['bootout', `${domain}/${plan.id}`])
|
|
102
|
+
}
|
|
103
|
+
for (const file of plan.files) {
|
|
104
|
+
mkdirSync(dirname(file.path), { recursive: true })
|
|
105
|
+
writeFileSync(file.path, file.content, { mode: 0o600 })
|
|
106
|
+
}
|
|
107
|
+
if (plan.platform === 'darwin') run('launchctl', ['bootstrap', domain, plan.files[0].path])
|
|
108
|
+
else {
|
|
109
|
+
run('systemctl', ['--user', 'daemon-reload'])
|
|
110
|
+
run('systemctl', ['--user', 'enable', '--now', `${plan.id}.timer`])
|
|
111
|
+
}
|
|
112
|
+
return { ok: true, action: 'installed', ...plan }
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
export function uninstallSchedule(options = {}, { run = defaultRun, uid = process.getuid?.() } = {}) {
|
|
116
|
+
const plan = schedulePlan(options)
|
|
117
|
+
if (plan.platform === 'darwin') {
|
|
118
|
+
let loaded = false
|
|
119
|
+
try { run('launchctl', ['print', `gui/${uid}/${plan.id}`]); loaded = true } catch { /* absent */ }
|
|
120
|
+
if (loaded) run('launchctl', ['bootout', `gui/${uid}/${plan.id}`])
|
|
121
|
+
} else run('systemctl', ['--user', 'disable', '--now', `${plan.id}.timer`])
|
|
122
|
+
for (const file of plan.files) rmSync(file.path, { force: true })
|
|
123
|
+
if (plan.platform === 'linux') run('systemctl', ['--user', 'daemon-reload'])
|
|
124
|
+
return { ok: true, action: 'uninstalled', repo: plan.repo, id: plan.id }
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
export function runDaily({ repo, olderThanDays = 30, apply = false } = {}, run = spawnSync) {
|
|
128
|
+
if (!isAbsolute(repo ?? '')) throw new Error('--repo must be absolute')
|
|
129
|
+
const args = [RETENTION, apply ? 'apply' : 'plan', '--repo', realpathSync(repo), '--generated-days', '7', '--artifact-days', String(days(olderThanDays)), '--json']
|
|
130
|
+
if (apply) args.push('--apply')
|
|
131
|
+
const result = run(process.execPath, args, { encoding: 'utf8', timeout: 25 * 60 * 1000, maxBuffer: 4 * 1024 * 1024 })
|
|
132
|
+
if (result.error) throw result.error
|
|
133
|
+
return { status: result.status ?? 1, stdout: result.stdout ?? '', stderr: result.stderr ?? '' }
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
export function main(argv) {
|
|
137
|
+
try {
|
|
138
|
+
const [command, ...rest] = argv
|
|
139
|
+
const options = {}
|
|
140
|
+
for (let i = 0; i < rest.length; i++) {
|
|
141
|
+
if (rest[i] === '--apply') { options.apply = true; continue }
|
|
142
|
+
if (!['--repo', '--older-than-days'].includes(rest[i])) throw new Error(`Unknown argument ${rest[i]}`)
|
|
143
|
+
const flag = rest[i]
|
|
144
|
+
const value = rest[++i]
|
|
145
|
+
if (!value || value.startsWith('--')) throw new Error(`${flag} needs a value`)
|
|
146
|
+
options[flag === '--repo' ? 'repo' : 'olderThanDays'] = value
|
|
147
|
+
}
|
|
148
|
+
if (!options.repo) throw new Error('--repo is required')
|
|
149
|
+
options.repo = resolve(options.repo)
|
|
150
|
+
if (command === 'run') {
|
|
151
|
+
const result = runDaily(options)
|
|
152
|
+
process.stdout.write(result.stdout)
|
|
153
|
+
process.stderr.write(result.stderr)
|
|
154
|
+
return result.status
|
|
155
|
+
}
|
|
156
|
+
if (options.apply) throw new Error('--apply is only valid with run')
|
|
157
|
+
const result = command === 'install' ? installSchedule(options)
|
|
158
|
+
: command === 'uninstall' ? uninstallSchedule(options)
|
|
159
|
+
: command === 'plan' ? schedulePlan(options) : null
|
|
160
|
+
if (!result) throw new Error('Usage: daily-cleanup.mjs install|uninstall|plan|run --repo PATH [--older-than-days 30] [--apply]')
|
|
161
|
+
process.stdout.write(`${JSON.stringify(result, null, 2)}\n`)
|
|
162
|
+
return 0
|
|
163
|
+
} catch (error) {
|
|
164
|
+
process.stderr.write(`daily-cleanup: ${error.message}\n`)
|
|
165
|
+
return 1
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
if (isMainModule(import.meta.url)) process.exitCode = main(process.argv.slice(2))
|
|
@@ -20,7 +20,8 @@
|
|
|
20
20
|
// --line, any change to the file counts)? Prints { inDiff, do }: `inDiff` true means fix it in this unit.
|
|
21
21
|
// add: append one entry; the coordinator is the only writer. An implementer entry inside the
|
|
22
22
|
// unit's diff is refused (`refused` in the output) because the unit caused it. A review entry inside the diff
|
|
23
|
-
// is kept and marked `inDiff`: the
|
|
23
|
+
// is kept and marked `inDiff`: the coordinator selects supporting work by the delivery
|
|
24
|
+
// criterion, not location or severity alone. The same location, kind and
|
|
24
25
|
// summary (or, for a review, the same unit and finding id) is one entry. `--decision` records
|
|
25
26
|
// a question for the human: never posted, listed in the handback. Prints { id, duplicate, inDiff }.
|
|
26
27
|
// from-review: add every P2 in a review result that has no followUpUrl or dropboxId, and
|