Skip to content

Excavator: TEMPORARY headless Argon path via agy CLI (follow-up to #173) - #174

Merged
sleegme merged 2 commits into
mainfrom
feat/excavator-agy-headless
Oct 25, 2026
Merged

sleegme merged 2 commits into
mainfrom
feat/excavator-agy-headless

Conversation

@sleegme

@sleegme sleegme commented Oct 25, 2026

Copy link
Copy Markdown
Owner

Summary

TEMPORARY alternative path for Excavator after #173 (owner decision). Native
Excavator stays disabled (subagent: false, mainAgent: false): AGY 1.3.6+
native subagent model: only accepts inherit/flash_lite/flash/pro, and pro and
flash both run gemini-3.8-flash-tiered. Bulldozer instead invokes Excavator
headlessly through the agy CLI on an exact policy-resolved Argon slug, with
loud fallbacks to the existing BLOCKED/NEEDS_DEEP path.

Design

  • scripts/excavator-agy.mjs — TEMPORARY headless runner. Bulldozer calls
    it via run_command from the milestone workspace:
    node <plugin-root>/scripts/excavator-agy.mjs --milestone <id> --blocker "<observed failure>".
    • Packet source: read-only from the authoritative ledger
      (--ledger/NTG_SUPERVISOR_LEDGER + --milestone; the packet is built
      with the same accessors spine's delegate() uses), or --packet <path|-> for direct use. Never retyped by the caller, never written.
    • Slug: resolveSlug("excavator") — a new excavator entry in the frozen
      ROLE_POLICY_TABLE (family "Gemini 4 Argon", effort High), resolved
      at runtime against agy models, fail-closed on 0/ambiguous matches. This
      is exactly how the runner picks Argon for Piledriver/Steamroller and how
      ntg-vnext picks Zen's exact slug. High matches Piledriver's
      difficult-decision tier (Argon High); Excavator's difficult
      broken-behavior diagnosis/repair is the same effort class. excavator is
      added to NATIVE_ROLE_BODY_ROLES only: invoke()/composeBoundedPrompt()
      still reject it (it is not in ROLE_BODY_ROLES), so this grants no runner
      or delegation authority.
    • Invocation: agy --model <slug> --output-format stream-json --print-timeout <timeout-30>s --dangerously-skip-permissions -p "<excavator contract + packet + blocker>" with cwd = milestone workspace. The prompt is the
      shipped agents/excavator.md body (frontmatter stripped) plus a machine
      invocation assignment, mirroring ntg-vnext's nativeZen shape. Outer
      spawnSync timeout kills anything agy's own print-timeout misses.
    • Result: parses the stream-json result event (deltas as fallback) and
      returns Excavator's worker result in the shape Bulldozer's contract
      already expects (ROOT_CAUSE labeled CONFIRMED/LIKELY/SPECULATIVE,
      CHANGES, VERIFICATION_EVIDENCE, ROLLBACK, RESIDUAL_RISK,
      STATUS READY|BLOCKED). Exit 0 = valid worker result; exit 1 = typed
      failure.
    • Visibility: every attempt appends
      <workspace>/.ntg/excavator-agy-calls.jsonl with started_at/ended_at/
      duration/model/exit_status/ok/error/worker_status/milestone_id/usage.
  • agents/bulldozer.md — hard-repair path routes to the script via
    run_command (the only shell exception, marked TEMPORARY) before falling
    back to BLOCKED/NEEDS_DEEP. Bobcat keeps ordinary repairs. run_command
    added to Bulldozer's frontmatter tools for native-path consistency.
  • Failures never pass silently — typed error codes:
    AGY_MISSING (ENOENT), RATE_LIMITED (429/quota/resource-exhausted),
    TIMEOUT (ETIMEDOUT/outer bound), UNPARSEABLE_OUTPUT (no result event /
    no parseable worker JSON), INVALID_WORKER_RESULT (contract shape),
    AGY_NON_SUCCESS, AGY_EXIT_NONZERO, UNRESOLVED_MODEL_SLUG (model absent
    from installed surface), RECURSION_DETECTED (NTG_RUNNER_CHAIN guard, same
    mechanism as the runner). All exit non-zero → Bulldozer falls back to the
    existing evidenced BLOCKED / NEEDS_DEEP path and cites the error code.
  • TEMPORARY marking in excavator.md, bulldozer.md, AGENTS.md,
    rules/orchestration.md, docs/internals.md, docs/status.md,
    docs/ko/status.md, docs/model-profiles/gemini-4-argon.md and the vNext
    architecture contract: revert to a native Argon subagent (and delete the
    headless path + run_command exception) once agy allows native Argon
    selection.

Evidence

  • .omo/evidence/174-excavator-headless-probe.md — one bounded live probe:
    a real agy -p Argon call through the script on a tiny synthetic repair
    (gemini-4-argon-high, broken add() fixture), with the worker result,
    call-log telemetry and post-repair check, paths redacted.

Tests

  • New tests/test_excavator_agy.mjs (31 tests, agy fully stubbed): CLI arg
    parsing incl. env overrides and mutual exclusion, packet validation,
    ledger/file/stdin assignment loading, prompt composition, argv building
    (stream-json, print-timeout, -p last), stream-json parsing (result event,
    delta fallback, junk tolerance), worker-result extraction (bare/fenced/
    wrapped; typed invalids), failure classification (ENOENT/ETIMEDOUT/429
    variants/plain exits), end-to-end success + BLOCKED, all failure modes
    with telemetry records, recursion guard, runner-chain propagation, CLI
    exit codes.
  • Updated tests/test_role_guidance.mjs (bulldozer/orchestration byte pins;
    bulldozer now must carry run_command scoped to the script, TEMPORARY) and
    tests/test_runner_spike.mjs (excavator joins the Argon fail-closed test).
  • Full suite: 321 tests, 300 pass, 0 fail, 21 skipped (baseline 269/0/21;
    +31 = the new file). Python guards: 23 tests OK.

Not verified

  • No live Bulldozer → script → BLOCKED/NEEDS_DEEP fallback run inside a real
    ntg-vnext run milestone (needs a full milestone with a genuinely hard
    repair); the routing is contract text + tested script behavior, and the
    Temporarily disable Excavator pending native Argon support #173 BLOCKED/NEEDS_DEEP path itself is already covered by
    tests/test_ntg_vnext.mjs.
  • Argon quota-exhaustion (429) was verified only against stubbed stderr
    patterns, not by exhausting the real quota.
  • The headless child's compliance with the NTG_EXCAVATOR=1 marker contract
    inside a real repair is covered by the deterministic guard tests; the probe
    fixture needed no privileged commands, so the guard did not fire in it.

Revert

Re-enable natively once a bounded native-subagent probe demonstrates Argon in
the child's actual model metadata, then in one change: set the verified native
tier + subagent: true, restore Bulldozer's native routing and spine
allowlists, and delete the headless script, its tests, the run_command
exception and the TEMPORARY notices.

Follow-up to #173 (owner decision). Native Excavator stays disabled
(subagent: false): AGY 1.3.6+ native pro/flash both run
gemini-3.8-flash-tiered. Bulldozer instead invokes the retained Excavator
contract headlessly through scripts/excavator-agy.mjs on the exact
policy-resolved gemini-4-argon-high slug (same frozen ROLE_POLICY_TABLE
resolution as Piledriver/Steamroller/Zen), inside the milestone workspace.

- scripts/excavator-agy.mjs: ledger/file/stdin packet (read-only),
  stream-json agy call with --print-timeout + outer timeout, worker-result
  parsing in the shape Bulldozer expects, JSONL call telemetry in
  .ntg/excavator-agy-calls.jsonl, typed failures (AGY_MISSING,
  RATE_LIMITED, TIMEOUT, UNPARSEABLE_OUTPUT, INVALID_WORKER_RESULT,
  RECURSION_DETECTED, ...) that fall back to BLOCKED/NEEDS_DEEP.
- bulldozer.md: hard-repair path routed to the script via run_command
  (single TEMPORARY shell exception); excavator stays not natively
  invocable; TEMPORARY notices across agents, rules, AGENTS.md and docs.
- tests: new test_excavator_agy.mjs (31 tests, agy stubbed); role-guidance
  pins and Argon fail-closed list updated. Suite: 321 tests, 300 pass,
  0 fail, 21 skipped; python guards 23 OK.
- evidence: one bounded live Argon probe (READY, CONFIRMED root cause,
  fixture repaired, check exits 0) in .omo/evidence/174-*.

Revert to a native Argon subagent once agy allows it.
Copilot AI balanced review requested due to automatic review settings October 25, 2026 08:59

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has insufficient remaining quota.

Gate review 89e3080 requested changes; each fix is locked with a
failing-first test.

B1 (C1/C6): spine runMilestone rejected declared headless Excavator
delegations for DONE, BLOCKED and NEEDS_DEEP. A delegation entry is now
accepted only with the sanctioned transport marker (exported
HEADLESS_EXCAVATOR_TRANSPORT = 'scripts/excavator-agy.mjs'); a bare
excavator role still claims native spawning and stays rejected.
BLOCKED/NEEDS_DEEP outcomes persist through the durable ledger path
with their evidence; DONE enters independent review. bulldozer.md now
instructs declaring the call with the marker; ntg-vnext's
declared-delegation projection accepts the same marked entries.

B2 (C5/C6): validateWorkerResult accepted READY with only STATUS and
ROOT_CAUSE. It now enforces the full worker-result contract: array
CHANGES (non-empty for READY), array VERIFICATION_EVIDENCE (non-empty
for READY), non-empty RESIDUAL_RISK, and ROLLBACK required whenever
persistent changes were made (non-empty CHANGES). The reviewer's exact
incomplete input now returns INVALID_WORKER_RESULT.

B3 (C7): call-log write failures were swallowed. recordTelemetry now
reports logged/logError; a valid worker result whose required log write
fails becomes an explicit unsuccessful outcome (ok:false,
TELEMETRY_LOG_FAILED, exit 1) with the worker result preserved in
worker_result; transport failures keep their primary error plus
telemetry_logged:false and telemetry_error.

The agy invocation itself is unchanged; the live probe was not
repeated. Suite: 339 tests, 318 pass, 0 fail, 21 skipped; python
guards 23 OK.
@sleegme

sleegme commented Oct 25, 2026

Copy link
Copy Markdown
Owner Author

Gate fixes for the three blockers in the REQUEST_CHANGES review (reviewed head 89e3080), each locked with a failing-first test:

B1 (C1/C6) — declared headless delegations rejected every status. runMilestone now accepts a declared Excavator delegation only when it carries the sanctioned transport marker (transport: "scripts/excavator-agy.mjs", exported as HEADLESS_EXCAVATOR_TRANSPORT), distinguishing the headless call from a native spawn claim — a bare excavator role is still rejected exactly as before. DONE with a declared headless delegation now enters independent review; BLOCKED/NEEDS_DEEP are preserved through the durable ledger path (blockers/escalation content intact, no Zen call). agents/bulldozer.md now instructs Bulldozer to declare the call in delegations with the marker, and ntg-vnext's declared-delegation projection accepts the same marked entries. Tests: DONE/BLOCKED/NEEDS_DEEP preservation, allowed-list case with marker, plus four rejection cases (bare role, wrong transport, other script, non-string transport) — all four rejection tests passed before the fix and still pass.

B2 (C5/C6) — READY without material fields passed validation. validateWorkerResult now enforces the full worker-result contract at the external-output boundary: array CHANGES (non-empty for READY), array VERIFICATION_EVIDENCE (non-empty for READY), non-empty RESIDUAL_RISK always, and ROLLBACK required whenever persistent changes were made (non-empty CHANGES), in addition to the existing STATUS/labeled-ROOT_CAUSE checks. The reviewer's exact input {"STATUS":"READY","ROOT_CAUSE":"CONFIRMED repaired"} now returns INVALID_WORKER_RESULT (→ BLOCKED/NEEDS_DEEP fallback). BLOCKED may carry empty CHANGES/VERIFICATION_EVIDENCE but still requires ROOT_CAUSE and RESIDUAL_RISK.

B3 (C7) — call-log write failures were silent. recordTelemetry now reports {logged, logError} instead of swallowing. A valid worker result whose required call-log write fails is an explicit unsuccessful outcome: ok:false, error:"TELEMETRY_LOG_FAILED", exit 1, with the worker result preserved in worker_result and the log error in telemetry_error — never a silent success. Transport failures keep their primary typed error and additionally report telemetry_logged:false + telemetry_error.

The live probe is unchanged and not repeated: the agy invocation itself (args, model, prompt transport) is byte-identical; only output validation and telemetry reporting changed. The checked-in probe result carries all contract fields, so it remains valid under the stricter validator.

Full suite after fixes: 348 tests, 327 pass, 0 fail, 21 skipped; python guards 23 OK.

@sleegme
sleegme merged commit cbdf416 into main Oct 25, 2026
4 checks passed
@sleegme
sleegme deleted the feat/excavator-agy-headless branch October 25, 2026 09:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants