Skip to content

feat(*): open the deck engine route on an attached template, not on a tier - #574

Merged
Tchen-data merged 4 commits into
mainfrom
feat/deck_engine_route_opens_on_template
Sep 21, 2026
Merged

Tchen-data merged 4 commits into
mainfrom
feat/deck_engine_route_opens_on_template

Conversation

@Tchen-data

@Tchen-data Tchen-data commented Sep 21, 2026 •

Copy link
Copy Markdown
Contributor

Summary

The route from Raven-Design to the Raven-PPT template engine used to open on the max tier alone. The same request then came back in three unrelated styles across the tiers, and the top one was built on a template the user never asked for. This PR moves the decision to the one thing that actually calls for the engine: a .pptx the user handed over.

  • Route on an attached template. SubagentRouteConfig gains needsFile (a suffix), a third independent condition beside needs and minTier. Raven-Design's route to Raven-PPT now declares needsFile: ".pptx" and drops minTier: "max". Every tier designs its deck on Raven-Design; the engine is reached only when a .pptx was handed over, either a bundled template picked in the web UI (companion PR feat(*): deck template picker and full-frame deck preview for the web ui #573) or one the user attached.
  • Handed over means attached in this turn. A new turn_attachments ContextVar is frozen beside turn_tier on the turn path. A direct chat's media all count; a spawn or DAG node counts an attachment only when its task text names that attachment's path or file name. Task text alone never opens the route: a deck brief spells the deck's destination path, the format in words, or a stray file in the directory exactly like a template, and the first text-only scan sent a no-template run into the engine on its destination path.
  • Tiers change effort, not product. medium asks for low reasoning effort, max for xhigh (the highest word a model may know; models that do not know it fall back to their default), and the baseline inherits the host's setting as before.
  • Design lane retries after streamed output. llmRetryAfterOutput: true on the design lane's defaults, matching the ppt lane. A max run died at its thirtieth iteration to an upstream idle timeout after the body had already streamed to the host; the lane now retries instead of failing the turn.
  • Skill: use what the user handed over first. Documents and data are the material, pictures are the pictures, and an attached .pptx is the engine's work, routed before the skill is read.

Docs: CONTEXT.md admission paragraph, agents/README.md, regenerated schemas/subagent.schema.json.

Also removed: the engine's internal decision log (plugins-dist/ppt-engine/docs/ppt-raven-design.md), a single-language working journal for the team building the deck lanes that has no reader in a public repository. The maintainer keeps it outside the tree; the two code comments that cited it by path now state their reason in place.

Type

  • Fix
  • Feature
  • Docs
  • CI / tooling
  • Refactor
  • Other

Verification

  • uv run --frozen pytest tests/test_subagent_routing_backend.py tests/test_agents_design_launcher.py tests/test_config_schema.py tests/test_subagent_registry.py -q -> 130 passed (after rebase onto main).

  • ruff check / ruff format --check on the changed Python files -> clean.

  • End to end on a full gateway: a high-tier request with a picked template logged classified for 'Raven-PPT'; routing there and the engine delivered a 20-page deck on that template in about 60 minutes; the same request without a template logged no .pptx the user attached was handed over; running here and stayed on Raven-Design. A brief that only spelled the deck's destination path stayed on Raven-Design.

  • Effort labels probed against z-ai/glm-5.3-flash over OpenRouter: low, high, xhigh all accepted; max spent the whole output window on reasoning, which is why the top tier is xhigh.

  • Relevant tests pass locally

  • Relevant lint / type checks pass locally

  • User-facing docs or screenshots are updated when needed

Risk

  • User-visible: medium and high tier deck requests no longer differ in product, only in effort; a deck on a template now requires attaching one. A bound direct-chat handle to Raven-Design stays on Raven-Design even with a .pptx attached (the route is evaluated at dispatch), a known boundary.

  • needsFile is additive; routes that do not declare it are dispatched exactly as before.

  • Rollback: revert the squash commit; no schema migration or storage change.

  • Security impact considered

  • Backward compatibility considered

  • Rollback path is clear for risky changes

Related Issues

N/A

… tier

The route from Raven-Design to the Raven-PPT template engine used to open
on the max tier alone, so the same request came back in three unrelated
styles across the tiers, and the top one was built on a template the user
never asked for. Every tier now designs its deck on Raven-Design; the
engine is reached only when the user handed over a .pptx, either a bundled
template picked in the web UI or one they attached themselves.

A route declares that with needsFile, a suffix that joins needs and minTier
as a third independent condition. Handed over means attached in this turn:
a direct chat's media, or an attachment of the turn that the spawn task
names by path or file name. The attachments are frozen beside the tier on
the turn path in a ContextVar, so a DAG node or a spawn reads the same set
its user sent. Task text alone never opens the route: a deck brief spells
the deck's destination path, the format in words, or a stray file in the
directory exactly like a template, and the first scan of this kind sent a
no-template run into the engine on its destination path.

What a tier buys is now how hard the lane thinks. Medium asks for low
effort and max for xhigh, the highest word a model may know; the baseline
inherits the host's setting as before. The design lane also retries an LLM
call that dropped after streaming output, which is how a run died at its
thirtieth iteration to an upstream idle timeout.

The deck skill says to use what the user handed over first: their
documents and data are the material, their pictures the pictures, and an
attached .pptx is the engine's work, routed before the skill is read.
@Tchen-data
Tchen-data requested a review from LivXue as a code owner September 21, 2026 03:22
@Tchen-data
Tchen-data requested a review from 0xKT September 21, 2026 03:22

@gloryfromca gloryfromca left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Blocking: make attachment-name matching reject suffixes of different filenames.

I found one blocking defect, marked inline.

I covered the full diff; AGENTS.md, CONTEXT-MAP.md, CONTEXT.md, and agents/README.md rules; spawn, DAG, and direct-chat callers; the prior tier/credential routing history; backward-compatible defaults and signatures; test changes (none were weakened to manufacture a pass); and the runtime/subagent architecture boundary.

Verification:

  • uv run pytest tests/test_subagent_routing_backend.py -q: 55 passed.
  • After uv sync --frozen --all-extras, the broader affected suite (routing, design launcher, schemas, manager, direct chat, DAG runner, and turn loop): 662 passed. The first attempt before syncing extras stopped on two missing-design-engine fixture failures; both passed after the declared extras were installed.
  • Ruff check and format check on all changed Python files: passed.
  • Source-language, large-file, commit-message, and git diff --check checks: passed.

Comment thread raven/agent/subagent/backends/routing.py Outdated
…ames it

The match that decides whether a spawn task hands over an attachment was
bounded only on the right, so the attachment brand.pptx was read as named
by a task that wrote new_brand.pptx, and a brief that told the lane to
ignore the attachment and save its own deck under a similar name opened
the engine route anyway. The name is now bounded on the left as well, and
the negative briefs include a prefixed output name and a hyphenated
neighbour.
The decision log under the engine's docs folder was a working journal for
the people building the deck lanes, written in one language for one team,
and it grew past two hundred kilobytes. It is not documentation of the
product and has no reader in a public repository; the maintainer keeps it
outside the tree from here on. The two places in code that cited it by
path say their reason in place instead.

@gloryfromca gloryfromca left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

No blockers; this can merge as far as I am concerned.

The follow-up commit fixes the reported left-boundary false positive and adds focused negative regressions without weakening existing coverage. Exact absolute-path, basename-only, and punctuated attachment handoffs still work. I found no new findings.

I rechecked the full diff and delta; AGENTS.md and the relevant CONTEXT/agent documentation; spawn, DAG, and direct-chat callers; routing history; backward compatibility; test changes; and the runtime/subagent architecture boundary.

Verification:

  • Affected suite: 662 passed.
  • Direct boundary probes: reported underscore and hyphen false positives rejected; valid path/name handoffs accepted.
  • Ruff check and format check: passed.
  • Source-language, large-file, commit-message, and git diff --check checks: passed.

@gloryfromca gloryfromca left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

No blockers; this can merge as far as I am concerned.

The attachment-boundary fix remains correct, and the latest cleanup commit only removes the internal design journal and makes its two former references self-contained. No dangling references remain and I found no new findings.

I reviewed the complete latest diff and the new delta; AGENTS.md and relevant CONTEXT/agent documentation; callers and routing history; backward compatibility; test changes; and the runtime/subagent architecture boundary.

Verification:

  • Latest-head targeted suite: 117 passed.
  • Prior full affected suite on the functional fix: 662 passed.
  • Direct attachment-boundary probes: passed.
  • Ruff, source-language, large-file, commit-message, and git diff --check checks: passed.

Comment thread raven/agent/subagent/backends/routing.py
@0xKT

0xKT commented Sep 21, 2026

Copy link
Copy Markdown
Member

Not a blocker, two observations from the same pass as the thread on routing.py. Neither holds up acceptance; both are things a later reader would have to re-derive.

(1) Max mode's user-facing text promises the top reasoning rung; the overlay declares the second one. This PR rewrites agents/raven-design/run.py:47 to "400 tool iterations at the highest reasoning effort the model offers" and repeats the claim in the source comment at run.py:191-192, while agents/raven-design/modes/max.json declares "reasoningEffort": "xhigh". xhigh is not the top of this repo's ladder: raven/contracts/llm_provider.py:57 is REASONING_EFFORT_LADDER = ("max", "xhigh", "high", "medium", "low", "minimal"), and both agents/raven-code/modes/max.json and agents/raven-oncall/modes/max.json declare "max" for their own Max tier. The text is not internal prose: raven/config/product_render.py:422-448 copies description verbatim into the rendered acp.modes entry and lifts reasoningEffort onto the same entry, so the label and the rung a user gets sit side by side in what the mode picker shows. Either declaration could be the right one -- the commit message's "the highest word a model may know" suggests xhigh may be deliberate, for a provider that does not take max -- but then the label should say what it actually buys rather than "the highest the model offers". At the base the overlay named no effort at all and the label said "the host's full reasoning effort", which was accurate; both halves changed here.

(2) The deleted design record is still cited 22 times, by number. a8685c8 removes plugins-dist/ppt-engine/docs/ppt-raven-design.md, which defined D24-D64 (its own line 3: "from D24 on, the ppt engine's design decisions are recorded here"; D1-D23 live in the retired fork tree and are unaffected). The commit cleans up exactly the two citations that spelled the file PATH -- plugins-dist/ppt-engine/raven_ppt/profiles/registry.py:23 and tests/test_ppt_engine_quiet_and_tier.py:3 -- and leaves every citation that names only a decision NUMBER. Four such numbers are still cited (D35, D38, D41, D52), 22 times across 13 files, of which 6 files are shipped engine source:

plugins-dist/ppt-engine/raven_ppt/profiles/registry.py:83            (D52)
plugins-dist/ppt-engine/raven_ppt/services/gates/house_style.py:20   (D52)
plugins-dist/ppt-engine/raven_ppt/services/gates/registry.py:179,383,403  (D52)
plugins-dist/ppt-engine/raven_ppt/services/gates/registry.py:435     (D35)
plugins-dist/ppt-engine/raven_ppt/services/measure/adherence.py:334  (D35)
plugins-dist/ppt-engine/raven_ppt/services/measure/layout.py:230     (D52)
plugins-dist/ppt-engine/raven_ppt/services/template/compose.py:902   (D41)

plus 13 more in tests/_ppt_engine_skill_claims.py, tests/test_agents_ppt_launcher.py, tests/test_ppt_engine_gates_house_style.py, tests/test_ppt_engine_gates_registry.py, tests/test_ppt_engine_measure_adherence.py, tests/test_ppt_engine_plugin.py and tests/test_ppt_engine_template.py. At the merge base every one of them resolved to a heading in the repository; at this head none of them does. Nothing breaks -- these are comments and docstrings -- but "(D52)" as the whole justification for a gate's severity is only useful while D52 can be read. grep -rn 'ppt-raven-design' finds the two that were fixed; grep -rnP '\bD[0-9]+\b' finds the twenty-two that were not. Whichever way you go -- keep the record, or strip the numbers where they are load-bearing and inline the reason -- the choice is worth making once rather than leaving the citations to rot.

For the record on the third one I checked and did not file: the ppt-engine's own _PROVENANCE comment (plugins-dist/ppt-engine/raven_ppt/profiles/registry.py:23) was rewritten in this PR to carry the reason inline instead of pointing at "docs/ppt-raven-design.md D2" -- which is exactly the right treatment, applied to one of the two readers in front of the author.

The right bound on the attachment-name match put the dot in the class of
characters that continue a name, so a brief ending its sentence with
brand.pptx and an ASCII full stop read as not naming the attachment, and
the template the user attached never reached the engine. A dot now closes
the name unless it opens a longer one: brand.pptx. and brand.pptx... name
the file, brand.pptx.bak, brand.pptx-based and brand.pptx_copy do not.
The spelling tests carry both sides.

The Max tier's label says what it buys, xhigh, instead of promising the
top of the ladder; xhigh is the rung kept because the providers this lane
runs against spend the whole output window thinking at max.
@Tchen-data

Copy link
Copy Markdown
Contributor Author

(1) Fixed in 88028db: the Max label now says "at xhigh reasoning effort", and the source comment says why that rung and not max (on the providers this lane runs against, max spends the whole output window thinking). The overlay was deliberate; the label was not.

(2) Agreed on making the choice once. The record leaves the repository by the maintainer's decision, so the 22 citations of D35/D38/D41/D52 will be replaced by the reason inline where they are load-bearing (the gate severities and the adapt removal) and dropped where they are not. That is a comment-only sweep across six engine files and seven test files, and it will land as its own chore change rather than widen this one.

@gloryfromca gloryfromca left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

No blockers; this can merge as far as I am concerned.

I independently verified the latest delta. Sentence-final punctuation after an attached filename now opens the route, longer filename suffixes and extensions remain rejected, and the Max label accurately describes the configured xhigh effort. The new cases extend coverage without weakening existing assertions. I found no new findings.

I rechecked the complete diff and latest delta; AGENTS.md and relevant CONTEXT/agent documentation; spawn, DAG, and direct-chat callers; routing history; backward compatibility; test changes; and the runtime/subagent architecture boundary. The other reviewer’s thread remains theirs to resolve.

Verification:

  • Affected suite: 662 passed.
  • Ruff check and format check: passed.
  • Source-language, large-file, commit-message, and git diff --check checks: passed.

@Tchen-data
Tchen-data merged commit 2d21c7d into main Sep 21, 2026
25 checks passed
@Tchen-data
Tchen-data deleted the feat/deck_engine_route_opens_on_template branch September 21, 2026 08:44
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.

3 participants