Skip to content

fix(ptc): teach only the tool surface a scope publishes - #930

Open
Nicholas023 wants to merge 1 commit into
T-Auto:mainfrom
Nicholas023:fix-batching-guidance-unpublished
Open

Nicholas023 wants to merge 1 commit into
T-Auto:mainfrom
Nicholas023:fix-batching-guidance-unpublished

Conversation

@Nicholas023

@Nicholas023 Nicholas023 commented Nov 14, 2026 •

Copy link
Copy Markdown
Collaborator

Closes #922

What was wrong

batchingGuidance shipped one lesson for every scope: step 3 told the model to
ops_load_sdk the names it was missing, and the bypass notice repeated the
same instruction. With dynamicTools: false the loader is never published, so
that step named a tool the scope cannot call.

Minimum reproduction: run ops-ptc with dynamicTools: false, send any
non-empty user message, and the first accepted batch carries a lesson whose
step 3 and table row both point at tools.ops_load_sdk. A model that follows
it spends one round trip and gets tools.ops_load_sdk is not a function —
the failure reported in #922. The same text reached the bypass notice, which
named five OPS APIs even in scopes that publish none of them.

The change

  • src/ptc/batching-guidance.ts: the renderer and the notice take a
    GuidanceSurface (loader, resident, loadable) and render from it.
    A loader-less scope gets a direct-call step in place of the load step, its
    table row, and any fenced example that teaches loading; the file-work line
    names only file APIs that are loadable or resident, never the shell it just
    accused. Section scanning tracks fences, and headings are matched by word so
    Payload budget is not mistaken for a load step.
  • src/publication/ptc.ts: builds the surface per agent at delivery time, so
    a narrow pool or a read-only policy cannot be told to load what it cannot
    reach; the read-only predicate is shared with tool registration.
  • test/: four suites cover the loader-less path end to end (live
    run_code row), the notice wording, the fence/anchor edges, and a
    read-only agent.

Deliberate non-goals, and why:

  • prompts/batching-guidance.md is untouched. The loader-present path stays
    byte-identical for a full pool (a test asserts equality), which keeps the
    shipped lesson and its cache intact.
  • A narrow allowedTools does not rewrite the shipped trajectories: the
    lesson is substituted only when the loader is absent. This is stated in
    docs/configuration-reference.md.
  • The lists follow the configured pool and the read-only policy, not the
    per-agent ownership narrowing. Also stated in the reference.
  • No new toggle: the behaviour is keyed on the existing dynamicTools and
    sandbox policy.

Validation

$ node scripts/verify-gate.mjs --skip-install
GATE_EXIT=0
  ok   versions
  ok   build the plugin
  ok   native worker unit tests
  ok   manifest contract
  ok   full suite verification
  ok   staged runtime closure
  ok   pack the release artifact
  244 suites passed; skipped: linux-attributes, linux-shell, tui-host, tui-native (allowed: file-metadata, linux-attributes, linux-shell, tui-host, tui-native)
  manifest: 92/59 checks
win32-x64: gate green

$ node test/run.mjs batching
dsh-ops batching-trigger: all 20 checks passed
dsh-ops batching-guidance: all 17 checks passed
dsh-ops file-batching-worker: all 8 checks passed
dsh-ops batching-examples: all 5 checks passed

$ node test/run.mjs resident-switch
dsh-ops resident-switch: all 26 checks passed

Behaviour-level red/green, not a structural check: with the built
lib/ptc/batching-guidance.js patched back to if (true) return text (the
pre-fix behaviour), the live suite fails:

  FAIL a loader-less ops-ptc row teaches only what it publishes
       AssertionError [ERR_ASSERTION]: the unreachable loader is gone
dsh-ops resident-switch: 1 of 25 check(s) failed

Restoring the file and rebuilding brings it back to 26/26.

  • docs/CHANGELOG.md has ## [Unreleased] entries in both languages; the
    bilingual configuration reference documents the two limits above.
  • No dependency added, package.json version and optionalDependencies
    unchanged.
  • Model-facing bytes: the shipped prompt file is unchanged, and the
    loader-present guidance is byte-identical; only the loader-less path and the
    notice text differ. No tool schema changed.
$ gh run list --branch fix-batching-guidance-unpublished --workflow ci.yml --event pull_request --limit 1
https://github.com/T-Auto/dsh-ops/actions/runs/43549647904

$ gh run view 43549647904 --repo T-Auto/dsh-ops --json url,event,conclusion --jq '"(.url) (.event) (.conclusion)"'
https://github.com/T-Auto/dsh-ops/actions/runs/43549647904 pull_request success

  job: CI required -> completed/success
  (all jobs: rust-lint, workflow-lint, tools-trial, source-size, node-floor,
   validate (linux-x64 / darwin-x64 / darwin-arm64 / win32-x64) — success)

Environment

  • Windows 11 x64, Node v24.21.0, npm 12.2.1
  • Based on upstream main at 88ecc72993e9e5c78597e63124b3bbcdf08b83dd (branch base 88ecc729)
  • Install channel: registry via npm ci --ignore-scripts

A scope without the loader was still taught to call `tools.ops_load_sdk`,
so every such session spent a round trip on a call that cannot succeed and
failed with "is not a function".

The guidance and the `bypass` notice now render from the surface the scope
actually publishes:

- a loader-less scope gets a direct-call step in place of the load step, its
  table row, and any fenced example that teaches loading, and says so when
  nothing is published at all;
- the file-work line names only the file APIs that are loadable or resident,
  never the shell it just accused, and a read-only policy drops the mutating
  APIs from that list;
- the surface is resolved per agent at delivery time, so a narrow pool or a
  read-only session is never told to load what it cannot reach.

The loader-present path is byte-identical for a full pool. Two limits are
documented in the reference: the shipped lesson itself is substituted only
when the loader is absent, and the lists follow the configured pool and the
read-only policy without the per-agent ownership narrowing.

Closes T-Auto#922
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.

[BUG] batchingGuidance 教模型使用未发布的 ops_load_sdk(dynamicTools: false)

1 participant