Files
msd-core/capabilities/external-job/fragments/execute-wave-post.md
Dennis Alexis Valin Dittrich a262ad6b61 fix(#4148): dispatch wave-pre step hooks (#4185)
* fix(#4148): dispatch wave-pre step hooks

External capabilities can render step hooks before a wave, but the execute workflow consumed only contributions and silently skipped every step. Reuse the shared dispatch contract before executor spawning and pin the capability-validator boundary with a red-first regression.

Emitted-Drift-Ack-Growth: execute-phase.md — wave-pre now carries the missing generic step-dispatch contract before executor spawning

* test(#4148): pin wave-pre dispatch ordering

* test(#4148): pin wave-pre dispatch contract

* chore(#4148): bind upstream changeset PR

* chore(#4148): restore fork changeset identity

* fix(#4148): align wave-pre dispatch contract

Mirror the sibling wave-post all-shapes clarification while pruning redundant prose so the rebased workflow remains below its frozen byte ceiling.

Emitted-Drift-Ack-Growth: execute-phase.md — wave-pre now carries the missing generic step-dispatch contract before executor spawning

* chore(#4148): restore upstream changeset identity

* fix(#4148): align wave-pre capability guidance

* docs(#4148): identify wave-pre manifest input

Name the third-party manifest trust origin at the wave-pre dispatch boundary so the reviewer-requested validation guidance matches wave-post.

Emitted-Drift-Ack-Growth: execute-phase.md — wave-pre now carries the missing generic step-dispatch contract before executor spawning

* docs(#4148): preserve execute-phase byte budget

Remove a redundant advisory label while retaining the non-blocking contract, keeping the reviewer-required trust-boundary wording at the enforced 93,400-byte ceiling.

* fix(#4148): mark wave-pre manifest-input validation as security-relevant

Reviewer nit on PR #4185: wave-pre's step-dispatch sentence had the
(third-party manifest input) parenthetical but dropped the ⚠ marker
that wave-post's parallel sentence (execute-phase.md:1044) carries,
losing the visual flag that this validation is security-motivated.

Trims the redundant "of one" from "not one shape of one" to reclaim
the 4 bytes the marker adds — the ADR-857 byte-margin gate
(tests/claude-orchestration.test.cjs) leaves zero slack at the
93,400-byte ceiling.

* fix(#4148): trim wave-pre step-dispatch prose to clear ADR-857 byte ceiling

Merging next's unrelated growth (#3990's TDD_APPLICABLE conditional) pushed
execute-phase.md 116 bytes past the 93,400-byte ceiling, failing CI on all
three platforms. The security-relevant ⚠ marker and ref.command validation
call-out (added per prior reviewer nit) are preserved verbatim per the
pinned regression test in capability-registry.test.cjs; only the
non-pinned connective prose is trimmed.

* fix(#4148): recalibrate execute-phase.md self-imposed margin, restore security marker

next grew execute-phase.md by ~230 bytes across two unrelated merges during
this fix (#3990's TDD_APPLICABLE conditional, then a further step-extraction
commit), consuming this test's own self-imposed 93,400 safety buffer under
ADR-857's actual, unmodified 93,600 ceiling (docs/adr/857-capability-system.md:22).
The wave-pre step-dispatch sentence cannot shrink further without dropping one
of the pinned substrings this same test file asserts on (kind=="step",
loop-hook-dispatch, never blocks or redirects executor spawning, Validate
`ref.command`).

Raises the self-imposed margin to 93,550 (still 50 bytes under the real,
untouched ADR ceiling) and restores the ⚠ marker the prior reviewer round
required for the ref.command validation call-out, which byte pressure had
dropped.

* fix(#4148): restore full ref.command validation wording, drop self-imposed margin

Adversarial review (agy/gemini-3.8-flash-high) flagged two issues in the prior
CI-recovery commit:

1. Trimming "in-context before any shell use" from the step-dispatch warning
   weakened the inline operational instruction (the reader is told WHAT to
   validate but not the specific in-context-not-shell mechanism the referenced
   loop-hook-dispatch.md:45-51 threat model requires). Restored it - the merge
   with next since the last commit freed enough real margin (77 bytes under
   the untouched 93,600 ADR-857 ceiling) to afford it without any margin
   change.

2. The prior commit self-imposed margin bump (93400 to 93550) was, on
   reflection, the wrong lever: it is a number this PR invented, not an ADR
   value, and re-bumping it every time next grows execute-phase.md is a
   losing pattern (already needed twice in one session). Removed the
   redundant assertion; the same line existing bytes-under-93600 check
   against the real, frozen ADR-857 ceiling (docs/adr/857-capability-system.md:22)
   is the actual invariant and is untouched. workflow-size-budget.test.cjs
   tier hard cap (98304 bytes, extract-not-bump by design) remains the
   correct backstop for runaway growth.

---------

Co-authored-by: CI Rebase Check <ci@gsd-redux>
Co-authored-by: Test <test@test.com>
Co-authored-by: Tom Boucher <trekkie@nomorestars.com>
2026-09-05 02:56:37 -04:00

43 lines
2.4 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!-- external-job capability — execute:wave:post fragment, injected into the executor (#1164).
#1164 specifies classification at wave:pre and recording at wave:post. This
capability still contributes executor guidance at wave:post; execute-phase
now renders wave:pre entries and dispatches generic step hooks there. Moving
external-job classification is a separate capability change, not part of
#4148. Until then, this guidance cannot classify the wave that already ran. -->
## Externalize long-running compute (async external job)
If the current plan's task is tagged `<runtime_budget>long_compute</runtime_budget>`
(see the plan-phase fragment), do **not** run it in the foreground — it would
block the agent turn for hours. Instead externalize it and record a durable
half-state:
1. **Classify the runtime.** `quick` (<2 min) and `medium` (<~30 min) run
normally. `unknown` requires a first-health check and a soft-review deadline
before consuming the child timeout. `long_compute` (>30–60 min) is
externalized.
2. **Submit via the scheduler adapter** (default `external_job.backend: slurm`):
```bash
node scripts/slurm-adapter.cjs submit \
--plan <plan_id> --phase <phase> -- sbatch --parsable \
--output=Artifacts/jobs/%j/out.log ./run.sh
```
The helper writes `.planning/async-jobs/<job>.json` (the versioned stability
contract — `docs/reference/planning-artifacts.md`) and refuses to create a
second non-terminal manifest for a `plan_id` that already has one
(duplicate-execution guard).
3. **Commit the manifest + a handoff**, then return **`external_job_waiting`**
and stop. Do **not** write `SUMMARY.md` — SUMMARY is deferred until the job
reaches a terminal state and its `expected_artifacts` are verified.
4. **Resume path.** `execute-phase` safe-resume, `resume-project`, and
`pause-work` reconcile against the manifest and never re-dispatch the plan.
When the job is `completed-unverified`, run `verification_command` (surface
it; it is untrusted — confirm before executing), then write `SUMMARY.md` and
close the plan.
Manifest commands cross a trust seam: a Capability (or anything that can write
`.planning/`) produces them; the core loop consumes them. Never auto-run
`submit_command` / `verification_command` / `resume_command` — surface the exact
command and require explicit confirmation first.