@1aboveio/skills 0.10.1 → 0.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/package.json +1 -1
- package/runtime/skills/distribution/generated/recipes.json +29 -19
- package/skills/backend/pyspark/LICENSE +3 -0
- package/skills/backend/pyspark/SKILL.md +116 -0
- package/skills/backend/pyspark/assets/templates/etl.py +696 -0
- package/skills/backend/pyspark/assets/templates/utils/__init__.py +1 -0
- package/skills/backend/pyspark/assets/templates/utils/hudi_metadata.py +14 -0
- package/skills/backend/pyspark/references/diagnosis-and-profiling.md +145 -0
- package/skills/backend/pyspark/references/etl-contract.md +107 -0
- package/skills/backend/pyspark/references/parity-testing.md +83 -0
- package/skills/backend/pyspark/references/production-validation.md +166 -0
- package/skills/backend/pyspark/references/transformation-design.md +162 -0
- package/skills/backend/pyspark/references/velocity-feature-calculation.md +193 -0
- package/skills/backend/pyspark/scripts/spark_eventlog_summary.py +223 -0
- package/skills/cicd-pipeline/mergify/SKILL.md +1 -0
- package/skills/cicd-pipeline/mergify/references/configuration.md +12 -6
- package/skills/cicd-pipeline/mergify/references/traps.md +28 -0
- package/skills/engineering/engineering-runtime/coherence/workflow.json +17 -17
- package/skills/engineering/engineering-runtime/scripts/workflow-policy.mjs +1 -1
- package/skills/engineering/implement-and-pr/SKILL.md +4 -3
- package/skills/engineering/implement-and-pr/agents/openai.yaml +9 -0
- package/skills/engineering/resolve-issues/SKILL.md +4 -3
- package/skills/engineering/resolve-issues/agents/openai.yaml +9 -0
- package/skills/engineering/resolve-issues/generated/workflow-repair-policy.json +11 -11
- package/skills/engineering/resolve-issues/references/pre-flight-model-slots.md +2 -2
- package/skills/engineering/resolve-issues/references/pre-flight-recording-and-checkout.md +1 -1
- package/skills/engineering/resolve-issues/references/pre-flight.md +1 -1
- package/skills/engineering/resolve-issues/scripts/preflight-questions.mjs +32 -9
- package/skills/engineering/resolve-issues/scripts/run-state.mjs +1 -1
- package/skills/engineering/resolve-release/references/preflight.md +3 -2
- package/skills/engineering/resolve-release/scripts/preflight-probes.mjs +13 -1
- package/skills/engineering/review-pr/SKILL.md +2 -1
- package/skills/engineering/review-pr/agents/openai.yaml +9 -0
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: resolve-issues
|
|
3
|
-
description: "Drive GitHub issues (single or epic) to merge-ready PRs: implement → e2e → independent review on a different model ∥ CI → fix, looped until
|
|
3
|
+
description: "Slash-command only (/resolve-issues). Drive GitHub issues (single or epic) to merge-ready PRs: implement → e2e → independent review on a different model ∥ CI → fix, looped until PASS; epics assemble one PR per shippable component. Orchestrates implement-and-pr, e2e-test, ensure-coverage, review-pr, smoke. Do not auto-select — only on explicit user/orchestrator invoke. NOT one implement+PR alone, review alone, rush-issues, or production release (resolve-release)."
|
|
4
|
+
disable-model-invocation: true
|
|
4
5
|
dependencies:
|
|
5
6
|
- implement-and-pr
|
|
6
7
|
- e2e-test
|
|
@@ -52,7 +53,7 @@ A step that serves none of these does not belong in this skill — see [The remo
|
|
|
52
53
|
|
|
53
54
|
- **Both model slots are filled individually by the human**, each as an operating point (a model *and* its banked effort), with alternates shown. **`reviewer ≠ implementer`** is hard — it is what makes guarantee 2 an independent review rather than a second opinion from the same blind spots.
|
|
54
55
|
- **Probe, never assume, the merge path** — `deliveryMode`, `queueProvider`, and above all **`enqueueTrigger`**: on a repo that auto-merges once conditions hold, *posting the review verdict as an approval **is** the merge*. Ask [`run-state.mjs approval-gate <slug> [componentId]`](scripts/run-state.mjs) before any verdict reaches GitHub. → [why](references/why.md#the-enqueue-trigger)
|
|
55
|
-
- **`autonomy` is
|
|
56
|
+
- **`autonomy` is filled as `autonomous` by default** (subsumes `mergeShippable`; guarantee 4). It is **not** a pre-flight question — stated in the summary; a human may override to `supervised`. Ask the manifest what it resolved to — `run-state.mjs autonomy <slug>` — never re-derive it from prose. No level lifts the design gate, the breaker's `descope`, or its round ceiling.
|
|
56
57
|
- **No silent fallback:** read back the model every spawn actually ran on and record it per unit. A reviewer read-back ≠ the confirmed review model means the verdict does not count.
|
|
57
58
|
|
|
58
59
|
## Routing (read this first)
|
|
@@ -111,7 +112,7 @@ A step that serves none of these does not belong in this skill ([the removal pat
|
|
|
111
112
|
Read [references/pre-flight.md](references/pre-flight.md) and run its seven steps (0-6) — **once**, before intake, never mid-run. One neutral question batch through [`scripts/preflight-questions.mjs`](scripts/preflight-questions.mjs); invoke only the calls it plans; record the completed patch atomically (a cancelled gate records nothing). The non-negotiables it elaborates rather than replaces:
|
|
112
113
|
|
|
113
114
|
- **Models** — the **implementer** is asked with alternates shown; the **reviewer is filled from the ranking, never asked**, and stated in the gate. **Reviewer ≠ implementer** is hard; a slot is an **operating point** (model + banked `models.<slot>Effort`). **No silent fallback:** read back the model each spawn ran on (`units[].implementationModel` when it differs) — a reviewer read-back ≠ the confirmed model voids the verdict for guarantee 2.
|
|
114
|
-
- **Everything else is probed, never assumed** — `targetBranch` from fetched remote heads; `deliveryMode` + `queueProvider` + `enqueueTrigger` from [`detect-delivery-mode.mjs`](scripts/detect-delivery-mode.mjs), UNKNOWN being a human ask; `autonomy`
|
|
115
|
+
- **Everything else is probed or filled, never assumed mid-run** — `targetBranch` from fetched remote heads; `deliveryMode` + `queueProvider` + `enqueueTrigger` from [`detect-delivery-mode.mjs`](scripts/detect-delivery-mode.mjs), UNKNOWN being a human ask; `autonomy` **filled** as `autonomous` by [`preflight-questions.mjs`](scripts/preflight-questions.mjs) (stated, overridable to `supervised`), subsuming `mergeShippable`, lifting neither the design gate nor the breaker's `descope` or ceiling. Where a repo auto-merges on its conditions, **posting the verdict as an approval *is* the merge** — ask [`run-state.mjs approval-gate <slug> [componentId]`](scripts/run-state.mjs) first.
|
|
115
116
|
- **Design first** — pre-flight **skims for units that already owe a design and hands them back** before anything else is settled.
|
|
116
117
|
|
|
117
118
|
An outer orchestrator that already ran an equivalent gate → inherit, don't re-prompt.
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
interface:
|
|
2
|
+
display_name: "Resolve Issues"
|
|
3
|
+
short_description: "Slash/explicit-only orchestrator: issues/epic → merge-ready PR(s)"
|
|
4
|
+
|
|
5
|
+
policy:
|
|
6
|
+
# Codex counterpart to SKILL.md disable-model-invocation: true
|
|
7
|
+
# (Claude Code / Pi). Keeps $resolve-issues / explicit invoke; blocks
|
|
8
|
+
# description-based auto-selection.
|
|
9
|
+
allow_implicit_invocation: false
|
|
@@ -236,7 +236,7 @@
|
|
|
236
236
|
"id": "first-party",
|
|
237
237
|
"type": "first-party",
|
|
238
238
|
"package": "@1aboveio/skills",
|
|
239
|
-
"version": "0.
|
|
239
|
+
"version": "0.12.0"
|
|
240
240
|
}
|
|
241
241
|
},
|
|
242
242
|
{
|
|
@@ -247,7 +247,7 @@
|
|
|
247
247
|
"id": "first-party",
|
|
248
248
|
"type": "first-party",
|
|
249
249
|
"package": "@1aboveio/skills",
|
|
250
|
-
"version": "0.
|
|
250
|
+
"version": "0.12.0"
|
|
251
251
|
}
|
|
252
252
|
},
|
|
253
253
|
{
|
|
@@ -258,7 +258,7 @@
|
|
|
258
258
|
"id": "first-party",
|
|
259
259
|
"type": "first-party",
|
|
260
260
|
"package": "@1aboveio/skills",
|
|
261
|
-
"version": "0.
|
|
261
|
+
"version": "0.12.0"
|
|
262
262
|
}
|
|
263
263
|
},
|
|
264
264
|
{
|
|
@@ -269,7 +269,7 @@
|
|
|
269
269
|
"id": "first-party",
|
|
270
270
|
"type": "first-party",
|
|
271
271
|
"package": "@1aboveio/skills",
|
|
272
|
-
"version": "0.
|
|
272
|
+
"version": "0.12.0"
|
|
273
273
|
}
|
|
274
274
|
},
|
|
275
275
|
{
|
|
@@ -280,7 +280,7 @@
|
|
|
280
280
|
"id": "first-party",
|
|
281
281
|
"type": "first-party",
|
|
282
282
|
"package": "@1aboveio/skills",
|
|
283
|
-
"version": "0.
|
|
283
|
+
"version": "0.12.0"
|
|
284
284
|
}
|
|
285
285
|
},
|
|
286
286
|
{
|
|
@@ -291,7 +291,7 @@
|
|
|
291
291
|
"id": "first-party",
|
|
292
292
|
"type": "first-party",
|
|
293
293
|
"package": "@1aboveio/skills",
|
|
294
|
-
"version": "0.
|
|
294
|
+
"version": "0.12.0"
|
|
295
295
|
}
|
|
296
296
|
},
|
|
297
297
|
{
|
|
@@ -302,7 +302,7 @@
|
|
|
302
302
|
"id": "first-party",
|
|
303
303
|
"type": "first-party",
|
|
304
304
|
"package": "@1aboveio/skills",
|
|
305
|
-
"version": "0.
|
|
305
|
+
"version": "0.12.0"
|
|
306
306
|
}
|
|
307
307
|
},
|
|
308
308
|
{
|
|
@@ -313,7 +313,7 @@
|
|
|
313
313
|
"id": "first-party",
|
|
314
314
|
"type": "first-party",
|
|
315
315
|
"package": "@1aboveio/skills",
|
|
316
|
-
"version": "0.
|
|
316
|
+
"version": "0.12.0"
|
|
317
317
|
}
|
|
318
318
|
},
|
|
319
319
|
{
|
|
@@ -324,7 +324,7 @@
|
|
|
324
324
|
"id": "first-party",
|
|
325
325
|
"type": "first-party",
|
|
326
326
|
"package": "@1aboveio/skills",
|
|
327
|
-
"version": "0.
|
|
327
|
+
"version": "0.12.0"
|
|
328
328
|
}
|
|
329
329
|
}
|
|
330
330
|
],
|
|
@@ -419,7 +419,7 @@
|
|
|
419
419
|
"sourceId": "first-party",
|
|
420
420
|
"sourceType": "first-party",
|
|
421
421
|
"package": "@1aboveio/skills",
|
|
422
|
-
"version": "0.
|
|
422
|
+
"version": "0.12.0",
|
|
423
423
|
"installPath": null,
|
|
424
424
|
"members": [
|
|
425
425
|
"harness-runtime",
|
|
@@ -435,7 +435,7 @@
|
|
|
435
435
|
"commands": [
|
|
436
436
|
{
|
|
437
437
|
"transport": "npm",
|
|
438
|
-
"command": "npx @1aboveio/skills@0.
|
|
438
|
+
"command": "npx @1aboveio/skills@0.12.0 install --group engineering-workflow"
|
|
439
439
|
}
|
|
440
440
|
],
|
|
441
441
|
"onFailure": {
|
|
@@ -19,7 +19,7 @@ Read this when pre-flight reaches step 1. The other steps stay in the index.
|
|
|
19
19
|
- **Propose only models that appeared in a discovery source or that the user named.** If discovery yields nothing, the proposal block must contain the *question* — "list the models available here" — with those slots left empty, not filled with plausible names: a guessed model (e.g. a Claude model in a harness that only runs one provider) is worse than no suggestion, because a confirmed-but-unrunnable model resurfaces mid-run as the silent-fallback failure the [no-silent-fallback guard](../SKILL.md#pre-flight-once-before-intake--never-mid-run) exists to catch. Your training-data knowledge of what models exist is not a discovery source.
|
|
20
20
|
|
|
21
21
|
|
|
22
|
-
2. **Classify the discovered models into capability tiers, then settle both slots in this gate.** The slots are **implementer · reviewer**. The **implementer is always asked** — the strength call, made per run against the run's risk profile; there is no pre-packaged profile. **The reviewer is never asked:** it is filled from a ranking (team default, then the diversity ladder), resolved against the answered implementer — `reviewer ≠ implementer` is the only possible collision and the ranking just moves down one. So the gate is **
|
|
22
|
+
2. **Classify the discovered models into capability tiers, then settle both slots in this gate.** The slots are **implementer · reviewer**. The **implementer is always asked** — the strength call, made per run against the run's risk profile; there is no pre-packaged profile. **The reviewer is never asked:** it is filled from a ranking (team default, then the diversity ladder), resolved against the answered implementer — `reviewer ≠ implementer` is the only possible collision and the ranking just moves down one. So the gate is **two questions on every run**: implementer and target branch. **Autonomy is filled as `autonomous`** (stated; overridable to `supervised`) — same “filled, not asked” shape as the reviewer. A harness that discovered a single model is still not asked (nothing to pick); it takes that id, recorded `NOT INDEPENDENT`. **Filled is not silent** — state the reviewer, autonomy, operating points, rank and any `reduced diversity` flag in the pre-flight summary, as with `workspaceMode` and `deliveryMode`, so an override costs one message. → [why](why.md#the-reviewer-slot-is-filled-not-asked)
|
|
23
23
|
|
|
24
24
|
**Mechanized, not described:** `preflight-questions.mjs` plans no `review` question in any shape it accepts and resolves the fill at `record`, reporting `reviewFill: { id, reason }` (`team-default` · `ladder` · `human-named` · `no-distinct-candidate`). Asking the reviewer is off-contract, not judgment.
|
|
25
25
|
|
|
@@ -38,4 +38,4 @@ Read this when pre-flight reaches step 1. The other steps stay in the index.
|
|
|
38
38
|
- **A slot is a model *and its operating point*** — each banked point is offered as its own slot candidate (`example-coder-2 @ high (balanced-coder)` · `example-coder-2 @ max (deep-reasoner)`), and the effort is **read from the catalog, never asked per run** (the effort rule and `high`-is-the-floor doctrine live in [model-catalog.md](model-catalog.md)). Record the picked effort next to the picked id (`models.<slot>Effort`) so a resume spawns at the same point.
|
|
39
39
|
- **Hard rules hold whatever the human picks:** reviewer **≠** implementer; the family preference never licenses an undiscovered model — a **single-provider harness** (e.g. Codex with only GPT-family models) picks a different model *within* the family and flags `reduced diversity — same family`; only one model available → the review is *not* independent, say so. A selection that would break a hard rule is **bounced back to the human with the conflict named — never silently repaired.** A model the human names that discovery didn't yield is taken as the human's own discovery source.
|
|
40
40
|
- **What the recommendation is based on (pre-intake):** a quick skim of the named issue(s) — title, labels, body — is enough to tell an auth/money/migration-heavy run or a declared refactor from routine feature work; cheap, and it doesn't move this gate after intake. If full intake later contradicts the confirmed implementer tier (a run confirmed on a balanced-coder implementer turns out predominantly high-risk once the sub-issues are enumerated), **surface a one-time re-confirm of the implementer slot then** — don't silently proceed on the wrong pick, and don't silently upgrade either; re-confirmation is the only strength lever, so a mis-called run gets exactly one deliberate correction.
|
|
41
|
-
- **Build one neutral question batch and let the shared runtime plan it.** Use [`scripts/preflight-questions.mjs`](../scripts/preflight-questions.mjs): the implementer question listing every eligible discovered **operating point**, followed by target branch
|
|
41
|
+
- **Build one neutral question batch and let the shared runtime plan it.** Use [`scripts/preflight-questions.mjs`](../scripts/preflight-questions.mjs): the implementer question listing every eligible discovered **operating point**, followed by target branch — **two questions**, on every run and every adapter, in one call. The reviewer ranking rides the same input (`review: { default?, candidates: [...] }`) and produces **no** question; autonomy is likewise filled (`defaulted.autonomy = autonomous`) with no question. The planner returns `reviewRanking`, `defaulted.review`, and `defaulted.autonomy` for the summary; `record` returns `reviewFill` and `autonomyFill`. Pass the exact `currentTurnCapabilities` from step 0; invoke only the native payload call(s) the returned `interaction.calls` plans, in order, and normalize the responses before recording. Recommended options stay first with one-line rationales. **Listing the alternates is load-bearing** — without them the human cannot see what discovery found and can only rubber-stamp; for the filled reviewer the alternates are the ranking, printed in the summary rather than as choices. Never drop or combine a slot to hide overflow.
|
|
@@ -6,7 +6,7 @@ what each `autonomy` level pre-authorizes, and the checkout/`workspaceMode` prep
|
|
|
6
6
|
happen before intake.
|
|
7
7
|
|
|
8
8
|
|
|
9
|
-
4. **Normalize, confirm or override, then record atomically** to the **run manifest** via `run-state.mjs merge` using `recordPreflightAnswers(...).manifestPatch` — `currentTurnCapabilities`, both model slots and their explicit families (plus each non-`high` effort), `targetBranch`, and `autonomy` — then add step 3a's `deliveryMode` (+ `queueProvider` in queue mode). Read back the manifest; do not hand-roll or partially bank the answer. Cancellation yields no patch and records nothing. A resumed run inherits the completed pre-flight instead of re-prompting. Identical model ids remain `NOT INDEPENDENT`; different ids in the same explicit family remain independent but are recorded as `reduced diversity`. **`autonomy
|
|
9
|
+
4. **Normalize, confirm or override, then record atomically** to the **run manifest** via `run-state.mjs merge` using `recordPreflightAnswers(...).manifestPatch` — `currentTurnCapabilities`, both model slots and their explicit families (plus each non-`high` effort), `targetBranch`, and `autonomy` — then add step 3a's `deliveryMode` (+ `queueProvider` in queue mode). Read back the manifest; do not hand-roll or partially bank the answer. Cancellation yields no patch and records nothing. A resumed run inherits the completed pre-flight instead of re-prompting. Identical model ids remain `NOT INDEPENDENT`; different ids in the same explicit family remain independent but are recorded as `reduced diversity`. **`autonomy` is filled, not asked** — `preflight-questions.mjs` defaults it to **`autonomous`** (stated in the summary; a human may override to `supervised` without a planned question). Completing pre-flight therefore records an authorization; an *unset* field on a manifest that never ran pre-flight still resolves to supervised elsewhere. Two levels:
|
|
10
10
|
|
|
11
11
|
| level | pre-authorizes | still stops and asks |
|
|
12
12
|
|---|---|---|
|
|
@@ -29,7 +29,7 @@ short probes the whole run rests on.
|
|
|
29
29
|
**Why this is step 0 rather than an assumption.** `SKILL.md`'s leaf-spawn contract requires every spawn to read `<skillsRoot>/<skill>/SKILL.md` at an absolute path, precisely because a relative `skills/engineering/...` path resolves to nothing once this skill is installed into a consumer repo by symlink — and the spawn then reconstructs a plausible-looking lookalike from memory instead of failing loudly. That contract had no step that ever produced the value: until 2026-07-20 `skillsRoot` appeared nowhere in this file, so every inline spawn's "absolute path" was aspirational and the orchestrator substituted the only path it had seen — the repo-relative one. The fan-out workflow hard-fails a relative path (`independent-review.workflow.js`); the inline path had no such guard, and the inline path is the default for single-unit runs.
|
|
30
30
|
|
|
31
31
|
|
|
32
|
-
3. **Detect and confirm the source (target/base) branch** in the same neutral batch. Settle it here, not at the first PR: every unit rests on this one branch — independent units open their PR against it directly, a [stacked dependent unit](intake.md#intake--scheduling) rests on it transitively, and the [epic integration gate](deliverables.md#deliverables-the-shippable-component-not-the-whole-epic) forks its integration branch off it. **Run `scripts/detect-target-branch.mjs`.** It fetches `origin` and recommends the branch that holds the current development head — applying, internally, the GitFlow-`dev`-vs-trunk-`main` choice and a *content* check (`git cherry`, not commit counts, so a GitFlow repo's nominal merge-commit divergence isn't mistaken for a conflict), and picking **nothing** on a genuine conflict. So the agent's job is only: **confirm its recommendation in this gate; on a `conflict` result, present the candidates it lists (each with ahead/behind + last-commit date) and let the human name the branch** — the script never picks on a genuine conflict, so a `conflict` always routes to the human. See `scripts/detect-target-branch.mjs --help` for flags and exit codes (`0` = recommendation, `1` = conflict/human chooses, `2` = usage / no long-lived branches). Implementer
|
|
32
|
+
3. **Detect and confirm the source (target/base) branch** in the same neutral batch. Settle it here, not at the first PR: every unit rests on this one branch — independent units open their PR against it directly, a [stacked dependent unit](intake.md#intake--scheduling) rests on it transitively, and the [epic integration gate](deliverables.md#deliverables-the-shippable-component-not-the-whole-epic) forks its integration branch off it. **Run `scripts/detect-target-branch.mjs`.** It fetches `origin` and recommends the branch that holds the current development head — applying, internally, the GitFlow-`dev`-vs-trunk-`main` choice and a *content* check (`git cherry`, not commit counts, so a GitFlow repo's nominal merge-commit divergence isn't mistaken for a conflict), and picking **nothing** on a genuine conflict. So the agent's job is only: **confirm its recommendation in this gate; on a `conflict` result, present the candidates it lists (each with ahead/behind + last-commit date) and let the human name the branch** — the script never picks on a genuine conflict, so a `conflict` always routes to the human. See `scripts/detect-target-branch.mjs --help` for flags and exit codes (`0` = recommendation, `1` = conflict/human chooses, `2` = usage / no long-lived branches). Implementer and branch are answered in the planned batch. The **reviewer** and **`autonomy`** slots are **filled rather than asked** — reviewer from the ranking / team default; autonomy defaults to `autonomous` (subsumes `mergeShippable`) and is stated in the summary, overridable to `supervised`. Every run therefore has exactly **two** questions — there is no worst case with three or four.
|
|
33
33
|
|
|
34
34
|
3a. **Probe the merge path — `scripts/detect-delivery-mode.mjs` — and record what it answers.** This is a **probe, not a fourth ask**: it costs no question budget and does not touch the one-interruption property step 3 just described. It settles two fields, because a queue is two different things: **`deliveryMode`** (`direct` | `queue`) picks the state machine that delivers a merge-ready unit, and **`queueProvider`** (`mergify` | `github`) picks the mechanics — the enqueue command, where queue state is read, what a dequeue reason is called, whether a re-evaluation nudge or an acknowledgement exists at all. Both fail silently in both directions when wrong, which is why they are probed rather than assumed and why the probe fails closed: **exit 2 is UNDETERMINED** — no probe could run, only file probes ran and were negative, or a queue whose provider is unknown/ambiguous — and *that* is the one case that becomes a human question, an exception path rather than a standing ask. A queue is a **runtime** fact whose config commonly lives dashboard-side, so never read either field off repo files.
|
|
35
35
|
|
|
@@ -118,12 +118,16 @@ export function planPreflightQuestions(value) {
|
|
|
118
118
|
const implementation = defineChoiceSet(value.implementation, 'implementation')
|
|
119
119
|
const target = defineChoiceSet(value.target, 'target branch')
|
|
120
120
|
const review = defineReviewRanking(value.review, 'review')
|
|
121
|
+
// Autonomy is FILLED, not asked — same shape as the reviewer slot. Completing
|
|
122
|
+
// pre-flight records `autonomous` (and therefore mergeShippable-on) unless the
|
|
123
|
+
// human overrides in the answer batch or prompt. An unset field on a manifest
|
|
124
|
+
// that never ran pre-flight still resolves to supervised elsewhere; this fill
|
|
125
|
+
// is what makes a completed pre-flight a recorded authorization.
|
|
121
126
|
const autonomy = Object.freeze({ recommended: 'autonomous', options: AUTONOMY_OPTIONS })
|
|
122
127
|
|
|
123
128
|
const questions = [
|
|
124
129
|
question('implementation', 'Implement', 'Which model should implement every unit in this run?', implementation),
|
|
125
130
|
question('target_branch', 'Target', 'Which fetched remote branch should every unit target?', target),
|
|
126
|
-
question('autonomy', 'Autonomy', 'How much reversible run activity should pre-flight authorize?', autonomy),
|
|
127
131
|
]
|
|
128
132
|
const interaction = planStructuredQuestions(capabilities, questions)
|
|
129
133
|
const metadata = Object.freeze(Object.fromEntries([
|
|
@@ -135,9 +139,11 @@ export function planPreflightQuestions(value) {
|
|
|
135
139
|
capabilities,
|
|
136
140
|
questions: Object.freeze(questions),
|
|
137
141
|
interaction,
|
|
138
|
-
//
|
|
139
|
-
|
|
140
|
-
|
|
142
|
+
// Filled slots stated in the pre-flight summary; `record` may honour overrides.
|
|
143
|
+
defaulted: Object.freeze({
|
|
144
|
+
review: review.candidates[0].id,
|
|
145
|
+
autonomy: autonomy.recommended,
|
|
146
|
+
}),
|
|
141
147
|
reviewRanking: Object.freeze(review.candidates.map((option) => option.id)),
|
|
142
148
|
freeTextQuestions: Object.freeze(interaction.kind === 'fallback'
|
|
143
149
|
? questions.map(({ id, prompt }) => Object.freeze({ questionId: id, prompt }))
|
|
@@ -226,13 +232,19 @@ export function recordPreflightAnswers(session, answerValue, { allowHumanNamed =
|
|
|
226
232
|
if (!isRecord(session) || !isRecord(session.selections)) invalid('question session is malformed')
|
|
227
233
|
const answers = defineQuestionAnswers(answerValue)
|
|
228
234
|
if (Object.keys(answers).length === 0) {
|
|
229
|
-
return Object.freeze({
|
|
235
|
+
return Object.freeze({
|
|
236
|
+
cancelled: true,
|
|
237
|
+
manifestPatch: null,
|
|
238
|
+
diversity: null,
|
|
239
|
+
reviewFill: null,
|
|
240
|
+
autonomyFill: null,
|
|
241
|
+
})
|
|
230
242
|
}
|
|
231
243
|
|
|
232
244
|
const expectedIds = session.questions.map((item) => item.id)
|
|
233
|
-
// `review`
|
|
245
|
+
// `review` and `autonomy` are never planned questions; a human who names either
|
|
234
246
|
// outright is honoured as an override rather than bounced.
|
|
235
|
-
const acceptedIds = [...expectedIds, 'review']
|
|
247
|
+
const acceptedIds = [...expectedIds, 'review', 'autonomy']
|
|
236
248
|
if (expectedIds.some((id) => !Object.hasOwn(answers, id))
|
|
237
249
|
|| Object.keys(answers).some((id) => !acceptedIds.includes(id))) {
|
|
238
250
|
invalid('answers must complete exactly the planned question batch')
|
|
@@ -250,8 +262,18 @@ export function recordPreflightAnswers(session, answerValue, { allowHumanNamed =
|
|
|
250
262
|
|
|
251
263
|
const targetId = singleAnswer(answers, 'target_branch')
|
|
252
264
|
selectedOption(session.selections.target, targetId, { allowCustom: customAllowed })
|
|
253
|
-
|
|
254
|
-
|
|
265
|
+
|
|
266
|
+
let autonomy
|
|
267
|
+
let autonomyFill
|
|
268
|
+
if (Object.hasOwn(answers, 'autonomy')) {
|
|
269
|
+
autonomy = singleAnswer(answers, 'autonomy')
|
|
270
|
+
selectedOption(session.selections.autonomy, autonomy)
|
|
271
|
+
autonomyFill = Object.freeze({ id: autonomy, reason: 'human-named' })
|
|
272
|
+
} else {
|
|
273
|
+
autonomy = session.defaulted?.autonomy || session.selections.autonomy.recommended
|
|
274
|
+
selectedOption(session.selections.autonomy, autonomy)
|
|
275
|
+
autonomyFill = Object.freeze({ id: autonomy, reason: 'default' })
|
|
276
|
+
}
|
|
255
277
|
|
|
256
278
|
const models = {}
|
|
257
279
|
modelPoint(models, 'implementation', implementation)
|
|
@@ -267,6 +289,7 @@ export function recordPreflightAnswers(session, answerValue, { allowHumanNamed =
|
|
|
267
289
|
}),
|
|
268
290
|
diversity,
|
|
269
291
|
reviewFill: Object.freeze({ id: review.id, reason: reviewFill.reason }),
|
|
292
|
+
autonomyFill,
|
|
270
293
|
})
|
|
271
294
|
}
|
|
272
295
|
|
|
@@ -3412,7 +3412,7 @@ export const WORKFLOW_PREFLIGHT_COMMANDS = Object.freeze([
|
|
|
3412
3412
|
|
|
3413
3413
|
const WORKFLOW_VERIFIER_URL = new URL('../../engineering-runtime/scripts/workflow-coherence.mjs', import.meta.url)
|
|
3414
3414
|
const WORKFLOW_FALLBACK_POLICY_URL = new URL('../generated/workflow-repair-policy.json', import.meta.url)
|
|
3415
|
-
export const WORKFLOW_TRUSTED_FALLBACK_POLICY_SHA256 = '
|
|
3415
|
+
export const WORKFLOW_TRUSTED_FALLBACK_POLICY_SHA256 = 'c6bde1d0b0e4c2bca363e396c5f0d1892996aa61e0531ae188626ebc6c971f3f'
|
|
3416
3416
|
const WORKFLOW_REPAIR_RECIPE_REFERENCE = Object.freeze({
|
|
3417
3417
|
id: 'engineering-workflow-dependency-first',
|
|
3418
3418
|
generatedFrom: 'skills/distribution/generated/recipes.json',
|
|
@@ -10,7 +10,8 @@ the human has confirmed this block. Indexed from
|
|
|
10
10
|
node <resolve-release>/scripts/preflight-probes.mjs check \
|
|
11
11
|
--repo <owner/name> --target-branch <prod-branch> \
|
|
12
12
|
--exposure-gate-probe '<the repo own check that its exposure profile can produce a verdict>' \
|
|
13
|
-
--topology-probe '<read-only command emitting the Cloud Build TAG_NAME substitution record>' \
|
|
13
|
+
[--topology-probe '<read-only command emitting the Cloud Build TAG_NAME substitution record>' | \
|
|
14
|
+
--deploy-trigger no-deploy] \
|
|
14
15
|
[--smoke-manifest <smoke.manifest.json> --smoke-secrets-command '<optional per-secret dry-fetch>'] \
|
|
15
16
|
[--gate '<a check-run that must be present and green on the target head>']
|
|
16
17
|
```
|
|
@@ -26,7 +27,7 @@ Exit **0** ready, **1** not ready (each probe naming its own remedy), **2** usag
|
|
|
26
27
|
| `rc-tag-topology` | The actual production path was a `TAG_NAME` Cloud Build trigger, while `_DEPLOYED_VERSION` had only been passed through a controller-submitted `gcloud builds submit`. The read-only command emits one representative RC record: `prodBuildTrigger`, `versionSource`, `candidateTagSource`, and `substitutions.TAG_NAME` / `_DEPLOYED_VERSION` / `_CANDIDATE_TAG`. It passes only for `tag-trigger` / `TAG_NAME` / `TAG_NAME`, with the final SemVer and RC candidate tag both derived from the RC `TAG_NAME`. |
|
|
27
28
|
| `smoke-secrets` *(optional)* | A smoke secret env var held the resource-name string instead of the secret value, so 4a would have returned `CANNOT-RUN` after the candidate was built. Caught before the rc tag when the exposure executor is this skill (#823). |
|
|
28
29
|
|
|
29
|
-
**`unknown` is not `ok`, and an absent `--exposure-gate-probe` or `--topology-probe` is `unknown`.** An unasked readiness question is [principle 13](principles.md#non-negotiable-principles)'s shape — an applicable gate that cannot run is a failure, not an absence — so a probe that was never supplied blocks the attempt exactly as a failing one does. A repo that never answers cannot silently release on the weaker path, which is the same rule item 3 applies to the candidate identity legs.
|
|
30
|
+
**`unknown` is not `ok`, and an absent `--exposure-gate-probe` or applicable `--topology-probe` is `unknown`.** An unasked readiness question is [principle 13](principles.md#non-negotiable-principles)'s shape — an applicable gate that cannot run is a failure, not an absence — so a probe that was never supplied blocks the attempt exactly as a failing one does. A repo that never answers cannot silently release on the weaker path, which is the same rule item 3 applies to the candidate identity legs. The explicit `--deploy-trigger no-deploy` shape is the sole exception to the RC topology read: it records `rc-tag-topology` as decided non-applicable because that shape mints no RC. Omitting the shape does not infer `no-deploy`, and supplying it does not waive credential, tag-divergence, target-gate, or exposure-capability probes.
|
|
30
31
|
|
|
31
32
|
**The topology probe is a pre-RC artifact, not a controller convenience.** Persist `preflight-probes.mjs check --json` and pass that file to `version.mjs rc --topology-probe <file>`; the mint refuses unless the artifact is ready and carries exactly `prodBuildTrigger: "tag-trigger"`, `versionSource: "TAG_NAME"`, and `candidateTagSource: "TAG_NAME"`. A controller-submitted build may pass `_DEPLOYED_VERSION`, but that does not prove a tag-triggered production build receives it. Likewise, a dev image keyed only by source SHA is informational: it does not satisfy candidate evidence before the RC tag/build/revision/tag binding exists.
|
|
32
33
|
|
|
@@ -167,7 +167,15 @@ export function classifyTargetGate({ checks, readable = true, gates = [] } = {})
|
|
|
167
167
|
// for this evidence.
|
|
168
168
|
const RC_TAG_RE = /^v?(\d+\.\d+\.\d+)-rc\.[1-9]\d*$/
|
|
169
169
|
|
|
170
|
-
export function classifyTagTopology({ topology = null, readable = true } = {}) {
|
|
170
|
+
export function classifyTagTopology({ topology = null, readable = true, deployTrigger = null } = {}) {
|
|
171
|
+
if (deployTrigger === 'no-deploy') {
|
|
172
|
+
return {
|
|
173
|
+
...probe('rc-tag-topology', PROBE_STATES.OK,
|
|
174
|
+
'no RC tag is minted for the explicitly declared no-deploy release shape'),
|
|
175
|
+
applicable: false,
|
|
176
|
+
deployTrigger,
|
|
177
|
+
}
|
|
178
|
+
}
|
|
171
179
|
if (!readable || topology == null || typeof topology !== 'object' || Array.isArray(topology)) {
|
|
172
180
|
return probe('rc-tag-topology', PROBE_STATES.UNKNOWN,
|
|
173
181
|
'the production build topology could not be read as a Cloud Build substitution record',
|
|
@@ -329,6 +337,7 @@ export function parseArgs(argv) {
|
|
|
329
337
|
if (a === '--repo') args.repo = argv[++i]
|
|
330
338
|
else if (a === '--target-branch') args.targetBranch = argv[++i]
|
|
331
339
|
else if (a === '--credential-command') args.credentialCommand = argv[++i]
|
|
340
|
+
else if (a === '--deploy-trigger') args.deployTrigger = argv[++i]
|
|
332
341
|
else if (a === '--exposure-gate-probe') args.exposureGateProbe = argv[++i]
|
|
333
342
|
else if (a === '--topology-probe') args.topologyProbe = argv[++i]
|
|
334
343
|
else if (a === '--smoke-manifest') args.smokeManifest = argv[++i]
|
|
@@ -371,6 +380,8 @@ a candidate is built (#823).
|
|
|
371
380
|
|
|
372
381
|
Options:
|
|
373
382
|
--credential-command <cmd> liveness check, default: ${DEFAULT_CREDENTIAL_COMMAND}
|
|
383
|
+
--deploy-trigger <shape> explicit release shape. no-deploy records RC topology as decided
|
|
384
|
+
not-applicable; omitting it retains the fail-closed RC topology read.
|
|
374
385
|
--exposure-gate-probe <cmd> the repo's own check that its exposure profile can produce a
|
|
375
386
|
verdict. ABSENT is "unknown", which is NOT ready.
|
|
376
387
|
--topology-probe <cmd> read-only repository command that prints JSON for one representative
|
|
@@ -434,6 +445,7 @@ export async function runCli(argv) {
|
|
|
434
445
|
probes.push(classifyTagTopology({
|
|
435
446
|
topology: topologyRead.ok ? safeJson(topologyRead.stdout) : null,
|
|
436
447
|
readable: Boolean(args.topologyProbe) && topologyRead.ok,
|
|
448
|
+
deployTrigger: args.deployTrigger || null,
|
|
437
449
|
}))
|
|
438
450
|
|
|
439
451
|
const exposure = args.exposureGateProbe ? sh(args.exposureGateProbe, 300_000) : {}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: review-pr
|
|
3
|
-
description: "
|
|
3
|
+
description: "Slash-command only (/review-pr). Adversarial pre-merge PR review → PASS / NEEDS_CHANGES / FAIL / BLOCKED with inline findings (spec'd or code-only). Do not auto-select — only on explicit user/orchestrator invoke. Not code-review (working-tree Standards∥Spec), and not ensure-coverage."
|
|
4
|
+
disable-model-invocation: true
|
|
4
5
|
dependencies:
|
|
5
6
|
- ensure-coverage
|
|
6
7
|
- e2e-test
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
interface:
|
|
2
|
+
display_name: "Review PR"
|
|
3
|
+
short_description: "Slash/explicit-only adversarial pre-merge PR review"
|
|
4
|
+
|
|
5
|
+
policy:
|
|
6
|
+
# Codex counterpart to SKILL.md disable-model-invocation: true
|
|
7
|
+
# (Claude Code / Pi). Keeps $review-pr / explicit invoke; blocks
|
|
8
|
+
# description-based auto-selection.
|
|
9
|
+
allow_implicit_invocation: false
|