Surfaces T-1061 orchestrator substrate state. Direct response to T-1641 trigger
("absolutely seeing nothing that indicates we are now orchestrating").
Sources: .context/audits/orchestrator-LATEST.yaml (T-1646) +
termlink list --json.
Generic view: /arcs/orchestrator-rethink — task table + §Arc Completion Discipline check.
{{ audit.findings.gate_drop_outs | join(', ') }}{{ audit.findings.new_unclassified_tools | join(', ') }}gated in baseline): {{ audit.findings.gate_added_ratchet_candidates | join(', ') }}{{ audit.findings.removed_tools | join(', ') }}| bad prefix | count | suggested | sample sessions |
|---|---|---|---|
{{ w.bad }} |
{{ w.count }} | {% if w.suggested %}{{ w.suggested }}{% else %}unrecognized{% endif %} |
{% for s in w.sample_sessions %}{{ s }}{% endfor %} |
PASS No drift. Baseline {{ audit.baseline_count }} tools tracked.
{% endif %}From .context/audits/orchestrator-mcp-baseline.yaml. T-1166 deprecation annotated in YAML comments — facades that will be removed are not gating targets.
{{ t }}{% endfor %}
{{ t }}{% endfor %}
bash agents/audit/orchestrator-mcp-scan.sh or
bin/fw audit --section orchestrator.
Make sure the TermLink hub is running locally. Routing/orchestration data is live-only; nothing is cached on this page.
| task-type | count |
|---|---|
{{ tt }} | {{ n }} |
task-type:<X>.
This is the symptom — orchestrator can route by task-type, but {{ untagged_sessions }} live session(s) carry no such tag. Tracked under T-1643 (Arc B — framework wiring).
| role | count |
|---|---|
{{ r }} | {{ n }} |
role:<X>.| name | state | task-type | role | model | task |
|---|---|---|---|---|---|
{{ s.name }} |
{{ s.state }} | {% for t in s.task_types %}{{ t }}{% endfor %} |
{% for r in s.roles %}{{ r }}{% endfor %} |
{% for m in s.models %}{{ m }}{% endfor %} |
{% for t in s.tasks %}{{ t }}{% endfor %} |
Per-model dispatch counts from .context/dispatches.jsonl
(CLI parity with fw orchestrator status, T-1788). Each row counts how
many times the framework dispatch path actually routed work to that model — the
"what got picked" view, complementing the learned-routing "what should win" view below.
Synthetic rows (T-stress-*) are excluded from the headline counts.
substrate absent No dispatches.jsonl at
{{ substrate.path }} yet. Run a dispatch (fw resolver dispatch
or fw termlink dispatch) to seed it.
no dispatches Substrate file exists but no real dispatches recorded yet.
{% else %}{{ substrate.total }} real dispatch(es){% if substrate.synthetic_total %},
plus {{ substrate.synthetic_total }} synthetic (T-stress-*,
excluded from the breakdown){% endif %}.
| Model | Dispatches | Share |
|---|---|---|
{{ row.model }} |
{{ row.count }} | {{ "%.0f%%"|format(100 * row.count / substrate.total) }} |
No model field on any dispatch row yet — substrate
rows predate the model-capture path or model wasn't resolved.
| Task-type | Dispatches | Share |
|---|---|---|
{{ row.task_type }} |
{{ row.count }} | {{ "%.0f%%"|format(100 * row.count / substrate.total) }} |
| Worker-kind | Dispatches | Share |
|---|---|---|
{{ row.worker_kind }} |
{{ row.count }} | {{ "%.0f%%"|format(100 * row.count / substrate.total) }} |
Verification pass/fail per task_type from
.context/dispatch-outcomes.jsonl (CLI parity with
fw orchestrator status --outcomes, T-1749). Latest outcome per
dispatch wins (T-1757 dedup rule). Verdict-style events (e.g.
escalation-scan-v0.5) are counted in Outcomes but only contribute to
passed/failed when they carry verification_passed.
no outcomes No outcome events recorded
yet (or dispatch-outcomes.jsonl is absent). Outcomes are
written back when dispatch workers exit.
{{ outcome_quality.total_outcomes }} outcome event(s) across {{ outcome_quality.by_task_type|length }} task-type(s).
{% if outcome_quality.by_task_type %}| Task-type | Passed | Failed | Total | Pass rate |
|---|---|---|---|---|
{{ row.task_type }} |
{% if row.passed %}{{ row.passed }}{% else %}0{% endif %} | {% if row.failed %}{{ row.failed }}{% else %}0{% endif %} | {{ row.total }} | {% if row.passed + row.failed %}{{ "%.0f%%"|format(row.pass_rate * 100) }}{% else %}—{% endif %} |
Workflow → dispatcher coverage matrix. Each workflow under
.context/project/workflows/ declares an optional
worker_kind field; the spawn driver
(lib/spawn._DISPATCHERS) routes a subset of declarable values.
Any workflow that declares a worker_kind without a registered handler is
a runtime trap (T-1776 — surfaced as NotImplementedError).
T-1798 ratchets this from a runtime
trap to an audit-time visibility check; this panel renders the same data
on the web.
unavailable Helper not importable.
{% else %} {% if not workflow_coverage.ok %}FAIL {% if workflow_coverage.unroutable_workflows %} {{ workflow_coverage.unroutable_workflows | length }} of {{ workflow_coverage.workflows | length }} workflows declare an unroutable worker_kind. {% endif %} {% if workflow_coverage.pi_workflows_missing_provider %} {{ workflow_coverage.pi_workflows_missing_provider | length }} pi workflow(s) missing provider. {% endif %}
{% elif workflow_coverage.warn %}WARN All {{ workflow_coverage.workflows | length }} workflows route, but {{ workflow_coverage.stale_workflows | length }} workflow(s) stale (no dispatch in 90d) — consider deprecating.
{% else %}OK All {{ workflow_coverage.workflows | length }} workflows route to a registered dispatcher and pi workflows declare a provider.
{% endif %}| Workflow | worker_kind | provider | Routable | Last dispatched |
|---|---|---|---|---|
{{ w.name }} |
{% if w.worker_kind %}{{ w.worker_kind }}{% else %}— (interactive){% endif %} |
{% if w.provider %}
{{ w.provider }}
{% elif w.worker_kind == "pi" %}
missing
{% else %}
—
{% endif %}
|
{% if not w.worker_kind %} n/a {% elif w.worker_kind in workflow_coverage.routable %} yes {% else %} no {% endif %} | {% set is_stale = workflow_coverage.stale_workflows and (w.name in (workflow_coverage.stale_workflows | map(attribute='name') | list)) %}
{% if w.last_dispatched %}
{{ w.last_dispatched[:10] }}
{% if w.last_dispatch_task_id %}
({{ w.last_dispatch_task_id }})
{% endif %}
{% if is_stale %}stale{% endif %}
{% else %}
never
{% if is_stale %}stale{% endif %}
{% endif %}
|
Routable dispatchers: {{ workflow_coverage.routable | join(', ') }}.
Declarable but unroutable:
{% if workflow_coverage.declarable_but_unroutable %}
{{ workflow_coverage.declarable_but_unroutable | join(', ') }}
(declared in lib/resolver.VALID_WORKER_KINDS but missing from
lib/spawn._DISPATCHERS — declaring any of these in a workflow is a runtime trap).
{% else %}
none.
{% endif %}
Missing provider:
{{ workflow_coverage.pi_workflows_missing_provider | map(attribute='name') | join(', ') }}
(pi workflows without a provider field — lib/spawn._spawn_pi raises
SpawnError at runtime; T-1800 surfaces it at audit time).
Per-task-type model preferences derived from route-cache.json
({{ learned.path }}). Each row shows the model with the highest historical
success rate for that task type — the framework dispatch path
(T-1669) consults this cache before falling
back to FW_DISPATCH_MODEL_FOR_<TYPE> / FW_DISPATCH_MODEL_DEFAULT.
Outcomes (success / failure) are recorded back into the same cache after each
dispatch's worker exits, so the table grows as the agent dispatches more work.
cache absent No route-cache.json at {{ learned.path }} yet.
Spawn at least one fw termlink dispatch worker to seed it.
no recordings Cache file exists but no model_stats
have been recorded yet. The headline mechanic ("orchestrator picks model based on task_type +
historical success rates") needs at least one completed dispatch per task_type to start firing.
| Task-type | Best model | Success rate | Volume | Last used | All candidates |
|---|---|---|---|---|---|
{{ row.task_type }} |
{{ row.best.model }} |
{{ "%.0f%%"|format(row.best.rate * 100) }} ({{ row.best.successes }}/{{ row.best.total }}) | {{ row.best.total }} | {{ row.best.last_used or "—" }} |
{% for c in row.candidates %}
{{ c.model }} {{ "%.0f%%"|format(c.rate * 100) }}
{% endfor %}
|
{{ learned.total_stats }} model/task-type stat(s) total. Resolution order in
framework dispatch: explicit --model → route-cache best → env-per-type →
env-default → none. Recording uses atomic write + flock for concurrency safety.
Last {{ recent_dispatches|length }} fw termlink dispatch worker(s) from /tmp/tl-dispatch/.
model_used and fallback_used are populated at dispatch time by the framework
(T-1664) and by /opt/termlink's substrate
CLI (scripts/tl-dispatch.sh, /opt/termlink T-1442 commit 143cd870).
null means neither path resolved a value (no explicit --model and no
FW_DISPATCH_MODEL_DEFAULT / DISPATCH_MODEL_FOR_<TYPE> set).
| Name | Task | Task-type | Model (asked) | Model used | Fallback? | Status | Started |
|---|---|---|---|---|---|---|---|
{{ d.name }} |
{% if d.task %}{{ d.task }}{% endif %} |
{% if d.task_type %}{{ d.task_type }}{% endif %} |
{% if d.model %}{{ d.model }}{% else %}—{% endif %} |
{% if d.model_used %}{{ d.model_used }}{% else %}null{% endif %} |
{% if d.fallback_used == True %}yes{% elif d.fallback_used == False %}no{% else %}null{% endif %} | {{ d.status }} | {{ d.started }} |
No recent dispatches. Run fw termlink dispatch --task T-XXX --name foo --prompt "..." to populate.
Filed by T-1641 (multi-agent reconsideration of T-1061). Three arcs split the work so reviewers see policy / wiring / drift as distinct decisions, not one mega-arc.
{% for t in arc_tasks %}Background: T-1641 reconsideration artefact · /review/T-1641 for human decision · 10 worker reports under docs/reports/T-1641-worker-NN-*.md.