mirror of
https://github.com/qwibitai/nanoclaw.git
synced 2026-07-06 18:52:03 +08:00
fix(setup): live-smoke fixes — anchor the channels-remote glob; silence operator blocks once blocked
Two bugs the first live wizard run surfaced: - channels-remote.sh's unanchored `*qwibitai/nanoclaw*` glob matched sibling channel repos (qwibitai/nanoclaw-discord sorts first on multi-remote machines) and resolved the from-branch copy to a remote with no channels ref, failing every from-branch fetch. The pattern now anchors the repo-name tail (nanoclaw / nanoclaw.git only). - Once the run-health gate latched `blocked`, later nc:operator blocks still rendered — walking the human through "a pairing code is about to appear" and a readiness confirm for a step the engine had already gated. Blocked runs now skip operator directives: no event (so no URL offer / readiness confirm), no operatorMessages entry, recorded in skipped. Spec §2 updated. Suite 722 passed | 1 skipped. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -115,6 +115,12 @@ can derive its own.
|
||||
`res.operatorMessages`, then `await onEvent({type:'operator', …})` before
|
||||
evaluating the next directive. An unresolved `{{var}}` in the body defers the
|
||||
whole block before any event fires (today's behavior, `skill-apply.ts:787`).
|
||||
**Once the run is `blocked`** (an earlier bounce), operator directives are
|
||||
skipped instead — no event, no `operatorMessages` entry, recorded in
|
||||
`skipped`. Walking the human through steps whose side effects the run has
|
||||
already gated ("a pairing code is about to appear" → nothing appears) is
|
||||
actively misleading, and a failed run's manual-steps report must not include
|
||||
steps predicated on the failure.
|
||||
3. Every `onEvent` call is awaited; a rejection from `onEvent` is treated like
|
||||
any other throw at that directive (bounce, not crash). This applies to
|
||||
operator events too: a consumer that throws from `onEvent` **accepts the
|
||||
|
||||
@@ -762,6 +762,59 @@ describe('run-health gate (a bounce blocks later side effects)', () => {
|
||||
expect(gated).toHaveLength(2); // restart + step, both bounced by the gate
|
||||
});
|
||||
|
||||
// Once blocked, an operator block must not walk the human through steps the
|
||||
// run has already gated ("a pairing code is about to appear" → nothing
|
||||
// appears). No event ⇒ a consumer's URL offer / readiness confirm never
|
||||
// fires; no operatorMessages entry ⇒ a failed run's manual-steps report
|
||||
// omits steps predicated on the failure. A block BEFORE the failure still
|
||||
// renders normally.
|
||||
it('a bounce also silences later operator blocks — no event, no collected message', async () => {
|
||||
writeFileSync(
|
||||
join(gskill, 'SKILL.md'),
|
||||
[
|
||||
'# doomed walkthrough demo',
|
||||
'',
|
||||
'## Create the bot',
|
||||
'```nc:operator',
|
||||
'Make the bot first.',
|
||||
'```',
|
||||
'',
|
||||
'## Validate the credential',
|
||||
'```nc:run capture:who effect:fetch',
|
||||
'verify-cred',
|
||||
'```',
|
||||
'',
|
||||
'## Get ready to pair',
|
||||
'```nc:operator',
|
||||
'Open the bot — a pairing code is about to appear.',
|
||||
'```',
|
||||
'',
|
||||
'## Pair',
|
||||
'```nc:run effect:step capture:platform_id=PLATFORM_ID',
|
||||
'run-pair-step',
|
||||
'```',
|
||||
'',
|
||||
].join('\n'),
|
||||
);
|
||||
const events: string[] = [];
|
||||
const res = await applySkill(gskill, groot, {
|
||||
inputs: {},
|
||||
exec: (c) => {
|
||||
if (c === 'verify-cred') throw new Error('401 bad credential');
|
||||
},
|
||||
execStream: async () => ({ ok: true, fields: { PLATFORM_ID: 'x' } }),
|
||||
onEvent: (e) => {
|
||||
if (e.type === 'operator') events.push(e.text);
|
||||
},
|
||||
});
|
||||
|
||||
expect(events).toEqual(['Make the bot first.']); // pre-failure block rendered…
|
||||
expect(res.operatorMessages).toEqual(['Make the bot first.']); // …and collected
|
||||
expect(res.operatorMessages.join()).not.toContain('about to appear'); // doomed block silenced
|
||||
expect(res.skipped).toContain('operator: skipped after an earlier failure');
|
||||
expect(res.agentTasks.some((t) => /an earlier step did not complete/.test(t.reason))).toBe(true); // step still gated
|
||||
});
|
||||
|
||||
it('a deferred prompt does NOT block a later restart (headless rebuild stays runnable)', async () => {
|
||||
writeFileSync(join(gskill, 'SKILL.md'), DEFER_THEN_RESTART_SKILL);
|
||||
const cmds: string[] = [];
|
||||
|
||||
@@ -768,6 +768,16 @@ export async function applySkill(skillDir: string, root: string, opts: ApplyOpti
|
||||
continue;
|
||||
}
|
||||
if (d.kind === 'operator') {
|
||||
// Once the run is blocked, walking the human through further manual
|
||||
// steps is actively misleading — the side effects those instructions
|
||||
// lead up to ("a pairing code is about to appear") have already been
|
||||
// gated. Skip: no event (so a consumer's URL offer / readiness confirm
|
||||
// never fires), no operatorMessages entry (a failed run's manual-steps
|
||||
// report must not include steps predicated on the failed one).
|
||||
if (blocked) {
|
||||
res.skipped.push('operator: skipped after an earlier failure');
|
||||
continue;
|
||||
}
|
||||
// Always collect the human-facing instructions into the result so a
|
||||
// programmatic caller can relay/output them. {{vars}} render so a
|
||||
// resolved value can be shown (throws → deferred if a referenced var is
|
||||
|
||||
@@ -20,10 +20,14 @@ resolve_channels_remote() {
|
||||
return 0
|
||||
fi
|
||||
|
||||
# Anchor the repo-name tail: the pattern must match the nanoclaw repo itself,
|
||||
# never a sibling channel repo like qwibitai/nanoclaw-discord (which carries
|
||||
# no channels branch — an unanchored `*nanoclaw*` glob picked those on
|
||||
# multi-remote machines and broke every from-branch copy).
|
||||
local remote url
|
||||
while IFS=$'\t' read -r remote url; do
|
||||
case "$url" in
|
||||
*qwibitai/nanoclaw*|*nanocoai/nanoclaw*)
|
||||
*qwibitai/nanoclaw|*qwibitai/nanoclaw.git|*nanocoai/nanoclaw|*nanocoai/nanoclaw.git)
|
||||
printf '%s' "$remote"
|
||||
return 0
|
||||
;;
|
||||
|
||||
Reference in New Issue
Block a user