Skip to content

Read a workflow stage out of the page HEY serves for it, and write down the Go parity - #169

Merged
jeremy merged 5 commits into
mainfrom
rust-parity-stage-view
Sep 10, 2026
Merged

jeremy merged 5 commits into
mainfrom
rust-parity-stage-view

Conversation

@jeremy

@jeremy jeremy commented Sep 10, 2026 •

Copy link
Copy Markdown
Member

Card: hey-sdk Rust: parity audit vs Go and WorkflowStageView

Based on origin/main; independent of #165 and #168.

WorkflowStageView

Workflows::stage(workflow_id, stage_id) answers a WorkflowStageView — id, name, and topics: Vec<WorkflowStageTopic { staging_id, topic_id, subject, entry_count }> — parsed from the HTML the generated get_stage returns (that method still answers the page, unchanged). The parser mirrors parseWorkflowStageHTML in go/pkg/hey/workflows.go rule for rule: the element whose id is container_workflow_stage_{id} (else NotFound), its first h2 for the name, every element under it whose id is topic_<n> for a card, data-identifier for the staging id, first h3 for the subject, the first whitespace field of p.card__detail for the count; a card is left out when either id will not parse as a positive integer or the detail line does not start with a count, and text under sr-only / screen-reader-only / u-for-screen-reader / visually-hidden is left out of names and subjects. One addition Go gets by construction: a card inside a card is that card's content, not a card of its own.

Parser: scraper with default features off (html5ever, selectors, cssparser, ego-tree, tendril — a spec-compliant HTML5 parser, which is what Go's x/net/html is too; ~20 crates on the lock). WorkflowStageView::parse(html, stage_id) is public so a page fetched some other way can be read the same way.

Tests: the wire test uses Go's own fixture from TestWorkflowsService_GetStage (name without its screen-reader suffix, a full card, a sparse card, two cards with unparsable ids left out), a not-found page, and a parser test for the rules the fixture does not reach (count line without a count, empty detail, nested card, non-positive ids, surrounding markup, whitespace collapse).

Parity audit

rust/hey-sdk/PARITY.md is the checked-in Go → Rust mapping: every one of the 202 exported Go service methods against its Rust counterpart (131 generated methods, one per modelled operation and route, plus 121 conveniences), the behaviour the model declares and how each SDK honours it, the Rust-only surface, and the waivers.

Findings:

  • 200 of 202 Go methods already had a Rust route to the same request. The two gaps were GetStage parsing (closed here) and Topics.Move(topicID, boxID) — Rust only had the generated move_topic(id, &body) — closed here as Topics::move_to_box(topic_id, box_id) with a wire test.
  • Waived: Contacts.Clearances (deprecated in Go; clearances().summary() is the same read) and Client.BoxIDByKind (a client method in Go; Boxes::id_by_kind is the counterpart).
  • Behaviour model: Rust honours idempotency, empty-on, pagination style, retry policy and the HTML representation from the route table; Go honours idempotency and retry policy from its generated client and the rest by hand at each call site. Two metadata wrinkles noted, not acted on: generated.IsIdempotent("UpdateMessage") and behavior-model.json say idempotent while x-hey-idempotent.natural is false and both runtimes send it once; the Window pagination style is carried on GetCalendarRecordings but read as a Link page in both.
  • Conformance: 195 shared cases, both runners dispatch all of them.
  • Not added, deliberately: the Go-deprecated alias; Journal::list_page / Contacts::get_contact ergonomics over generated methods that already answer the same thing.

Gate

On the branch as pushed (on main at 1d3d047): make rs-check green, make rs-check-drift green, make conformance-rs 195/195.

Adversarial review

Codex CLI (gpt-6-astra, reasoning xhigh) review runs against this diff; findings and dispositions follow in a comment.


Summary by cubic

Adds Workflows::stage, which parses the HTML HEY serves for a workflow stage into a WorkflowStageView (stage id, name, and topic cards), mirroring Go's GetStage rule for rule, and closes the last two Go/Rust parity gaps with Topics::move_to_box and a checked-in parity audit. The shared conformance case now asserts the parsed stage in both runners, which is why Go's stage structs gained JSON tags.

New Features

  • The parser reads the stage container element, its first h2 for the name, and every topic_<id> element under it for a card; a card inside a card is that card's content, not a card of its own.
  • Parsing uses scraper (html5ever underneath), a spec-compliant HTML5 parser like Go's x/net/html.
  • A card is left out when its thread or staging id will not parse as a positive integer, or when its detail line does not start with a count.
  • Text under sr-only, screen-reader-only, u-for-screen-reader, or visually-hidden is left out of names and subjects.
  • WorkflowStageView::parse is public so a page fetched some other way can be read the same way.
  • WorkflowStageTopic and WorkflowStageView are #[non_exhaustive], so future fields are not a breaking change.
  • Topics::move_to_box(topic_id, box_id) mirrors Go's Topics.Move and sends the same body the generated move_topic carries.
  • PARITY.md maps all 202 Go service methods to their Rust counterparts, records how each SDK honours idempotency, empty-on, pagination, retry, and HTML behaviour, and lists the two waivers.

Written for commit 228f747. Summary will update on new commits.

Review in cubic

Copilot AI balanced review requested due to automatic review settings September 10, 2026 06:43
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 10, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-09-10T08:31:59.330587Z 228f747 New commits
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

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.

🟢 Approval recommended

The implementation matches the documented Go behavior and includes focused wire and parser coverage.

Pull request overview

Adds Rust parity with Go for parsed workflow stages and topic moves.

Changes:

  • Adds WorkflowStageView HTML parsing and tests.
  • Adds Topics::move_to_box.
  • Documents Go/Rust parity and adds parser dependencies.

[!TIP]
If you aren't ready for review, convert to a draft PR.
Click "Convert to draft" or run gh pr ready --undo.
Click "Ready for review" or run gh pr ready to reengage.

File summaries
File Description
rust/hey-sdk/src/services/workflows.rs Adds workflow-stage models and parser.
rust/hey-sdk/src/services/topics.rs Adds topic-move convenience.
rust/hey-sdk/src/services/mod.rs Re-exports stage models.
rust/hey-sdk/tests/services_workflows.rs Tests stage fetching and parsing.
rust/hey-sdk/tests/services_topics.rs Tests topic move requests.
rust/hey-sdk/PARITY.md Documents Go-to-Rust parity.
rust/hey-sdk/Cargo.toml Adds HTML parser dependencies.
rust/Cargo.lock Locks parser dependency graph.
conformance/runner/rust/Cargo.lock Updates runner dependencies.
Review details
  • Files reviewed: 7/9 changed files
  • Comments generated: 0
  • Review effort level: Balanced

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

@chatgpt-codex-connector chatgpt-codex-connector Bot 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 7493cc8734

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread rust/hey-sdk/src/services/workflows.rs Resolved
@jeremy

jeremy commented Sep 10, 2026

Copy link
Copy Markdown
Member Author

Codex CLI (gpt-6-astra, reasoning xhigh) reviewed the diff after it opened; findings and what was done (afc881a):

  • P1, unbounded recursion over server-controlled nesting (Go's parser refuses nesting past 512; html5ever does not) — fixed: visible_text walks the subtree iteratively over ego_tree edges. Test: 200,000 nested <span>s inside the heading.
  • P2, p.card__detail matched the exact class where Go matches the substring — fixed: a p whose class mentions card__detail. Test: card__detail--compact.
  • P2, descendant selectors skipped the starting element where Go's findNode includes it — fixed: first_at_or_under looks at the element itself first, for the stage heading, the card subject and the detail line. Tests: an h2 that is itself the stage, an h3 that is itself the card.
  • P2, the ancestor scan ran past the stage — fixed: it stops at the stage element.
  • P3, class splitting on ASCII vs Go's Unicode whitespace — fixed: split as strings.Fields does. Test: a non-breaking space in the class.
  • P3, u64 parse vs Go's Atoi — fixed: parsed as i64 and refused when negative, so -0 is zero and -1 skips the card, as in Go.
  • P3, duplicate stage ids in repaired markup select in insertion order rather than tree order — declined: two elements carrying the same stage id is not a page HEY renders, and neither SDK promises which one wins.
  • PARITY.md — five corrections applied (form posts resend once after a refreshed 401; a reply draft may have no recipients; delete_occurrence_scoped is hand-written; only Unfile omits a zero folder id; 122 conveniences), and the missing-stage error's shape (NotFound here, a plain error in Go) is now recorded.

The Codex GitHub reviewer's P1 (ancestors() includes the node itself, so every card is dropped) is declined in-thread with the evidence. The last commit also carries the three #[cfg(feature = "tracing")] lines from #172 so the gate passes on today's main; it becomes a no-op once #172 lands.

@chatgpt-codex-connector chatgpt-codex-connector Bot 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: afc881a0aa

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread rust/hey-sdk/src/services/workflows.rs Resolved
…wn the Go parity

Workflows::stage answers a WorkflowStageView — the stage's name and its thread
cards — parsed from the HTML get_stage returns, by the same rules Go's GetStage
reads it: the element whose id names the stage, its first h2 for the name,
every topic_<id> element for a card with its staging id, subject and email
count, screen-reader text left out, a card whose ids will not parse left out.
scraper (html5ever underneath, as Go uses x/net/html) does the parsing.

Topics::move_to_box takes a bare box id, the one thing the generated
move_topic's body carries, as Go's Topics.Move does.

PARITY.md maps every Go service method to its Rust counterpart, says which
behaviour the model declares and how each SDK honours it, and lists the
Rust-only surface and the two waivers.
@jeremy
jeremy force-pushed the rust-parity-stage-view branch from afc881a to 2120114 Compare September 10, 2026 07:57

@chatgpt-codex-connector chatgpt-codex-connector Bot 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 2120114e38

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread rust/hey-sdk/src/services/workflows.rs Resolved
@jeremy
jeremy merged commit 18ea502 into main Sep 10, 2026
25 checks passed
@jeremy
jeremy deleted the rust-parity-stage-view branch September 10, 2026 08:50
@jeremy jeremy mentioned this pull request Sep 10, 2026
@jeremy jeremy added enhancement New feature or request rust Pull requests that update rust code labels Sep 10, 2026
jeremy added a commit that referenced this pull request Sep 10, 2026
Since v0.30.0 the Rust crate's response types and enums are #[non_exhaustive]
(#166, breaking), the Rust client honors each route's retry policy and the Go
client holds its retry settings as a ceiling over it (#154, #164), operations
carry tracing spans behind a feature (#165), a workflow stage is read out of
the page HEY serves (#169), and the crate publishes to crates.io on the tag
(#162). cargo semver-checks against v0.30.0 asks for the minor.

The bump script still looked for the README dependency line #160 replaced.
The READMEs now show the crates.io requirement, which names the minor, and
the git dependency pinned to the release tag, which names the whole version;
the script moves both and checks each.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request rust Pull requests that update rust code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants