Skip to content

feat(workspace): route warehouse tools through the bound workspace's engine - #1156

Draft
ralphstodomingo wants to merge 30 commits into
feat/workspace-engine-syncfrom
feat/workspace-precedence
Draft

feat(workspace): route warehouse tools through the bound workspace's engine#1156
ralphstodomingo wants to merge 30 commits into
feat/workspace-engine-syncfrom
feat/workspace-precedence

Conversation

@ralphstodomingo

@ralphstodomingo ralphstodomingo commented Aug 26, 2026

Copy link
Copy Markdown
Contributor

Issue for this PR

Closes #1155

Type of change

  • Bug fix
  • New feature
  • Refactor / code improvement
  • Documentation

What does this PR do?

Stacked on #1154 — review that first; this PR is the commits on top.

When a bound workspace's engine attaches, the model gets two ways to do the same thing: the native warehouse tools over local keychain connections, and the engine's MCP tools over the workspace's SaaS connection. Nothing chose between them, so the model picked whichever description read better — and that pick decided which credentials ran the query and whether it was audited. Engine calls are audited server-side; native ones aren't.

This adds a per-session decision: shadow only what materialised and is attributable to the bound workspace; anything undetermined runs locally and says why; nothing is silent. Why it works:

  • Materialised, not declared — it reads the engine tool keys actually present, so a declared-but-absent integration shadows nothing.
  • Attributed — an IDE writes its entry unpinned, and such an engine serves whichever teammate is active there, changing at runtime. feat(workspace): attach the bound workspace's integration engine #1154 makes attach guarantee the pin; this re-checks it and refuses to shadow otherwise, reusing that PR's parser rather than writing a second one.
  • Per capability, not per warehouse type — the integrations aren't symmetric. Snowflake has execute, explain and table stats; BigQuery and Postgres have execute only. Keying on the type would send an explain on a BigQuery connection to a tool that doesn't exist, and there is deliberately no fallback.
  • Calls naming no warehouse are judged against the target they'd really reach, mirroring each handler's own resolution — and the registry connection behind dbt is weighed too, since dbt falls back there whenever it yields nothing.
  • The redirect names the exact engine tool, executes nothing, and is marked in metadata so telemetry can tell it from a real execution.
  • It runs after every native safety check — the hard deny on destructive statements and the write confirmation both guard things the engine side has no equivalent for.
  • --integrations=local turns it off for a session.

Two deliberate deviations: the guard needs a companion call to attach the fail-open notice, which a pre-execution check can't do; and I left an adjacent warehouse-type reporting bug alone, since fixing it changes a shipped telemetry field.

How did you verify your code works?

bun run typecheck and the workspace test suites pass; lint adds no new errors. 250 unit tests, including each decision rather than just the happy path.

End-to-end against a live workspace, engine serving 12 tools:

Scenario Outcome
Shadow marking The served connection is marked per capability; local DuckDB rows unmarked
Redirect, nothing executed Redirect returned, and no matching row in query history — proven with a positive control: the marker from a run that followed the redirect appears, the one that stopped at it does not
Control untouched DuckDB connection runs normally
Default target No warehouse named → resolved the default and redirected, naming it
Model behaviour Unprompted, the model followed the redirect and ran the query through the engine tool
Write confirmation An INSERT against the served connection stops at the permission gate, with no redirect
Escape hatch --integrations=local → plain listing, local execution

Not exercised: the asymmetry case needs a workspace serving only some capabilities (unit-tested instead); the dbt default path needs a bound dbt project; de-materialisation is held until other testing here finishes.

Automated review: four rounds plus one routed in, six findings, all real, all fixed — details in the Codex review log.

GitGuardian flags a masked placeholder in a help-text snapshot that only moved columns; it is present unchanged on the base commit and is not a credential.

Screenshots / recordings

n/a — CLI change, no UI.

Checklist

  • I have tested my changes locally
  • I have not included unrelated changes in this PR

@ralphstodomingo ralphstodomingo self-assigned this Aug 26, 2026
@gitguardian

gitguardian Bot commented Aug 26, 2026

Copy link
Copy Markdown

⚠️ GitGuardian has uncovered 1 secret following the scan of your pull request.

Please consider investigating the findings and remediating the incidents. Failure to do so may lead to compromising the associated services or software components.

🔎 Detected hardcoded secret in your pull request
GitGuardian id GitGuardian status Secret Commit Filename
34260894 Triggered Generic CLI Secret 04e5180 packages/opencode/test/cli/help/snapshots/help-snapshots.test.ts.snap View secret
🛠 Guidelines to remediate hardcoded secrets
  1. Understand the implications of revoking this secret by investigating where it is used in your code.
  2. Replace and store your secret safely. Learn here the best practices.
  3. Revoke and rotate this secret.
  4. If possible, rewrite git history. Rewriting git history is not a trivial act. You might completely break other contributing developers' workflow and you risk accidentally deleting legitimate data.

To avoid such incidents in the future consider


🦉 GitGuardian detects secrets in your source code to help developers and security teams secure the modern development process. You are seeing this because you or someone else with access to this repository has authorized GitGuardian to scan your pull request.

@coderabbitai

coderabbitai Bot commented Aug 26, 2026

Copy link
Copy Markdown

Important

Draft PR not reviewed

Draft PRs are not automatically reviewed by default.

  • Trigger a manual review

To automatically review draft PRs, update your CodeRabbit configuration:

reviews:
  auto_review:
    drafts: true

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@ralphstodomingo

Copy link
Copy Markdown
Contributor Author

@codex review

@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: 7ecb94a92c

ℹ️ 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".

}

async function derive(tools: Record<string, unknown>): Promise<Precedence> {
if (escapeHatchOn()) return EMPTY("escape-hatch")

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Gate precedence on the workspace pilot flag

When ALTIMATE_WORKSPACE is unset after a previously opted-in run, the persisted workspace binding and pinned datamate MCP entry can still materialize engine tools, and this derivation enables routing because it checks only the escape hatch. This bypasses the documented opt-in gate and can unexpectedly redirect local warehouse calls for users who disabled the workspace pilot; return disabled precedence when CoreFlag.ALTIMATE_WORKSPACE is false.

Useful? React with 👍 / 👎.

// Adapter not initialised far enough to answer; leave the type undetermined.
}
if (!type) type = await adapterTypeFromManifest()
return { source: "dbt", type }

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Account for the native fallback when resolving dbt targets

When a warehouse-less sql_execute has a usable dbt adapter, this always reports the dbt target, but the actual handler falls back to Registry.list()[0] whenever dbt execution throws or returns an unrecognized result. If the dbt type is unshadowed while that registry connection is a shadowed type, precedence allows the call and the fallback then executes locally against a connection that should have been routed to the workspace engine. The routing decision must account for this fallback or be applied again at the point where the native fallback is selected.

Useful? React with 👍 / 👎.

@github-actions

Copy link
Copy Markdown

Thanks for updating your PR! It now meets our contributing guidelines. 👍

@ralphstodomingo
ralphstodomingo force-pushed the feat/workspace-precedence branch from 7ecb94a to bd7edfa Compare August 26, 2026 17:03
@ralphstodomingo

Copy link
Copy Markdown
Contributor Author

@codex review

@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: 8a017dd81f

ℹ️ 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".

// Ahead of the write-permission prompt on purpose: a shadowed write should be
// redirected, not approved and then redirected.
const precedence = await Precedence.check(ctx.sessionID, "sql_execute", args.warehouse)
if (precedence.redirect) return precedence.redirect

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Run the hard-deny check before redirecting

When workspace precedence shadows the selected connection, this return happens before classifyAndCheck, so DROP DATABASE, DROP SCHEMA, and TRUNCATE calls that previously threw the unconditional “cannot be overridden” error now instruct the model to invoke the engine execution tool instead. The MCP wrapper only checks permission for the engine tool name and does not apply this SQL hard-deny list, so following the redirect bypasses the native destructive-query safeguard; preserve the hard-deny check before deciding whether to redirect.

Useful? React with 👍 / 👎.

async execute(args, ctx) {
// altimate_change start — workspace precedence
const precedence = await Precedence.check(ctx.sessionID, "sql_explain", args.warehouse)
if (precedence.redirect) return precedence.redirect

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Validate explain inputs before redirecting

When the default or named warehouse is shadowed, this early return bypasses both preflight validators below it. Consequently, inputs such as an empty/placeholder SQL string or warehouse: "" return a successful-looking workspace redirect instead of the existing INVALID INPUT response, encouraging the model to pass malformed arguments to the engine tool; perform these input checks before consulting precedence.

Useful? React with 👍 / 👎.

@ralphstodomingo
ralphstodomingo force-pushed the feat/workspace-precedence branch from 5945afd to 5551619 Compare August 26, 2026 17:28
@ralphstodomingo

Copy link
Copy Markdown
Contributor Author

@codex review

@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: 5551619a37

ℹ️ 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 on lines +42 to +43
const precedence = await Precedence.check(ctx.sessionID, "sql_execute", args.warehouse)
if (precedence.redirect) return precedence.redirect

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Preserve SQL write approval across redirects

When the builder submits an INSERT, UPDATE, DELETE, or other non-hard-denied write against a shadowed connection, this returns before the sql_execute_write permission check, and the model's subsequent engine call is checked only under its datamate_* tool key (session/prompt.ts:1866-1872). The default builder rules allow that key through the wildcard rule while requiring sql_execute_write: "ask", so the redirected write can modify the workspace warehouse without the confirmation the same native call previously required; propagate or independently enforce the write permission before the engine executes.

Useful? React with 👍 / 👎.

@ralphstodomingo
ralphstodomingo force-pushed the feat/workspace-precedence branch 3 times, most recently from 83d0636 to e772cb7 Compare August 26, 2026 18:47
@ralphstodomingo

Copy link
Copy Markdown
Contributor Author

@codex review

@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: e772cb7da0

ℹ️ 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 on lines +357 to +358
const type = canonicalType(target.type)
if (!type) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Check the dbt fallback before returning on an unknown type

When the dbt adapter exists but neither getAdapterType() nor the manifest yields a recognized type, target can still include a shadowed registry fallback, yet this branch returns before that fallback is checked. If the subsequent dbt execution throws or returns an unrecognized result shape, sql.execute then executes against the first local connection, bypassing workspace routing and its server-side audit path. Fresh evidence after the earlier fallback review is that the unknown-type early return still precedes the added target.fallback handling; evaluate the fallback before returning the undetermined verdict.

Useful? React with 👍 / 👎.

@ralphstodomingo

ralphstodomingo commented Aug 26, 2026

Copy link
Copy Markdown
Contributor Author

Codex review log

Kept in one comment and edited in place, so the PR description stays short. Every finding below was confirmed against the code or the running system before anything was changed; each fix's test was checked against the pre-fix version and observed to fail.

Round 1 — 2 findings, both real

Finding Verdict Fix
Precedence wasn't gated on the workspace pilot flag Confirmed. The binding and the pinned entry both persist in config and the MCP client connects that entry regardless of the flag, so engine tools materialise for someone who opted out. Checked live: with the flag unset, an engine still spawned and served its tools Derivation returns disabled unless the pilot is on
The default-target resolver ignored the native fallback after dbt Confirmed. The dbt helper returns null on an unrecognised result shape and inside a bare catch, not only when dbt is absent, and the handler then falls through to the first registry connection The fallback is reported alongside the dbt target, and a served fallback redirects

Round 2 — 2 findings, both real

Finding Verdict Fix
The redirect jumped over the hard deny Confirmed, and a safety regression. DROP DATABASE, DROP SCHEMA and TRUNCATE are blocked with a message saying it cannot be overridden, and the engine tools apply no equivalent list — verified, neither resolver references the classifier. A blocked statement came back as an instruction to call the engine tool Deny runs first
The redirect jumped over explain's input validators Confirmed. A redirect reads as success, so malformed input was sent onward instead of reported Validators run first. Schema inspection has no pre-flight checks and is unaffected

Round 3 — 1 finding, real

Finding Verdict Fix
The redirect jumped over the write confirmation Confirmed. The write permission is "ask", but an engine tool key is matched by the builder agent's allow-all rule, so a redirected INSERT, UPDATE or DELETE reached the warehouse without the confirmation the same statement needed a moment earlier The guard runs last, after every native safety check

This corrected my own reasoning. I had put precedence ahead of the write prompt because approving a write that then redirects seemed to authorise something that never runs. The write does run — through the engine — and what the user authorises is the write, not which connection carries it.

Routed in from the stacked PR — 1 finding, real

Finding Verdict Fix
The fail-open notice was attached only to the success path Confirmed, and wider than reported. Review named three catch blocks; auditing every exit after the guard found three more. An undetermined target usually means a misconfigured setup, so those calls fail more often than average — the telemetry marker went missing in exactly the population it exists to measure Every failure exit carries it; schema inspection routes its failures through one helper so one cannot be overlooked

Round 4 — 1 finding, real

Finding Verdict Fix
The undetermined verdict returned before the fallback was weighed Confirmed. A dbt target whose type cannot be identified failed open even when the connection behind it is served — and an undetermined type makes reaching that fallback more likely, not less The fallback is weighed before giving up on the type

That was the third defect in the order of the same branches. The pattern was the real finding: each fix was correct and each reopened a sibling path, because reaching a dbt-sourced target through the live path needs a real dbt project, so the order was never directly testable. The decision is now a pure function with the order documented and its own suite.

Round 5 — 1 finding, real

Finding Verdict Fix
A redirect could send a caller to a tool it is not permitted to call Confirmed, in the code and against the running system. The analyst agent denies everything it does not name; it names the native warehouse tools but never the engine keys. So on a served connection its permitted reads were redirected to a tool it is forbidden to call — taking away the one thing that agent exists to do Shadowing now requires the destination to be reachable as well as materialised and attributed. All three redirect sites are gated; an absent ruleset means unknown and stays reachable

This is the counterpart to round 3. There, an agent that allows everything let a redirected write through without the confirmation the native path required. Here, an agent that denies everything unnamed made the redirect impossible to follow. The same redirect crossing the same permission boundary fails in opposite directions depending on the agent, which is the general lesson: a redirect is only sound if the destination is reachable under the caller's own rules.

Round 6 — 1 finding, real

Finding Verdict Fix
The listing claimed routing that would not happen Confirmed against the running system. After round 5, a caller denied the engine key had its calls run locally — but the warehouse listing still reported the connection as served by the workspace and counted it as shadowed. An analyst was being told to expect routing its own permissions rule out, in the very output the model reads before choosing a tool Every user-facing report goes through one helper returning the capabilities that materialised and whose destination this caller may call. Partial reachability is reported per capability

Round 5 fixed the routing and left the reporting behind it. Worth naming as its own class: when a decision gains a new condition, everything that describes that decision inherits the condition too, and the describing code is easy to forget because nothing it does is wrong on its own.

Round 7 — 2 findings, both real

Finding Verdict Fix
Attribution read the saved config rather than the running engine Confirmed. An MCP entry can be rewritten from unpinned to pinned while the already-connected process keeps serving, so the config names this workspace while the running engine serves another — precedence would then route calls into someone else's engine Attribution now requires the attach outcome as well as the pin: the outcome is the runtime signal, the pin catches a later rewrite naming somewhere else
The per-session caches were unbounded Confirmed. A long-running server sees an unbounded number of session ids and each entry holds a merged permission ruleset Same cap and insertion-ordered eviction as the attach module

The first is the same class as the pilot-flag and reachability findings: something outside this module can change under it, and reading the convenient signal rather than the authoritative one leaves a window. Verified against the running system before and after, because a wrong reading would have disabled routing everywhere silently rather than failing loudly.

Round 8 — 1 finding, real

Finding Verdict Fix
Attribution awaited the attach task Confirmed. Round 7's fix read the attach outcome by awaiting the attach itself, which is deliberately uncapped — the prompt loop bounds its own wait and lets a turn proceed without engine tools past the cap. Awaiting it hung every affected turn for the full connection timeout, reintroducing from this side the delay the attach work had just removed Read through the attach module's new read-only accessor instead. Not known yet and never attached are indistinguishable, and both fail open with a notice

Triage of this one took two false starts, both environmental rather than code: a shared fixture whose entry another session had disabled, and the shared engine binary crashing before it could print a version — which attach reports as "engine too old", a plausible product outcome rather than a broken toolchain. Two smokes were read as evidence against the fix before the environment was checked. The corrective is now a standing rule: before attributing a behaviour change to code, verify the fixture's entry and that the engine actually runs.

Round 9 — 2 findings, both real, both P2

Finding Verdict Fix
A tool's description claimed redirection for capabilities it does not have Confirmed. The claim was made whenever the workspace served any capability for that warehouse type, so an execute-only integration had explain and inspect described as redirecting while their bodies correctly stayed local — steering the model away from the local tool that works Each tool claims redirection only for its own capability; the listing keeps its whole-listing note
A stale inventory was never corrected Confirmed. The line was announced once per session. The first turn can legitimately say routing is off — an attach outrunning its bounded wait looks the same as no engine — and precedence is re-derived every turn, so the truth changes under a session already told otherwise The last announced line is remembered and compared: corrections are delivered, unchanged inventories stay quiet

Both are the same class as round 6 — when a decision gains a condition, the code describing it inherits the condition — this time at capability granularity rather than warehouse type. Three findings across the run have now been that class, which is why every user-facing report resolves through the same predicate the routing uses.

Round 10 — clean

No findings on a06841c5c. Nothing inline, no review, and the verdict arrived as a plain comment: "Didn't find any major issues." That closes the cycle: nine rounds produced thirteen confirmed findings, and the tenth — run against the commit the ninth had just modified — produced none.

Round 11 — 2 findings, both real

Finding Verdict Fix
P1 — a routing decision could outlive its binding Confirmed. Re-linking mid-session is supported, so the turn's snapshot can name a workspace the project has already left, and a redirect naming it would send the query to that workspace's engine with its credentials The binding is re-checked before any redirect — a local cache read, paid only on the path about to redirect
P2 — an evicted snapshot ran silently Confirmed. A session with no snapshot returned a bare "run", indistinguishable from a considered "not served", so an entry evicted between tool resolution and the call would let a shadowed connection execute locally with no indication Unknown now runs locally and says so, like every other undetermined case

The P1 is the within-turn drift recorded as a residual early in the design — it had been named but not closed, and review found the concrete consequence. Worth noting that a documented residual is not a handled one.

Round 12 — 1 finding, real

Finding Verdict Fix
P1 — routing ending was never announced Confirmed. The announcement compared inventories, which catches a capability appearing or disappearing but not routing stopping altogether: with nothing left to enumerate the line is empty, and an empty line was read as nothing to say. A session told its calls were routed kept believing it after the engine stopped serving anything The transition is stated explicitly, and only to a session that had previously been told otherwise

Third finding in the same class — the code that describes a decision keeps asserting what used to be true. Twice that was a stale claim; here it was silence, which is the same failure wearing different clothes.

Round 13 — 2 findings, 1 real, 1 refuted as stated

Finding Verdict Fix
P1 — recheck connections after concurrent registry updates Refuted as stated; reachable variant found and fixed. The claim was that a parallel warehouse.add could land between the routing decision and Registry.get(name). It cannot: for a named warehouse there is no suspension point anywhere on that path — the tool body does not await between the verdict and Dispatcher.call, the dispatcher's lazy-registration await is cleared after the first call in the process, the handler falls through to the registry with no await, and Registry.get reads its config synchronously before its own first await. Single-threaded JS cannot interleave. The warehouse-less path is a different story: it awaits the dbt attempt and only afterwards re-reads the registry, so the connection executed need not be the one the decision covered The handler read the registry twice, either side of an await. It now reads once, before it. Decided and executed connections are the same by construction, and a connection that disappears mid-flight is reported by name instead of quietly running somewhere else
P2 — an announcement that was not routing counted as routing Confirmed. Round 12's fix remembered the last announcement as a bare string, so any non-empty line counted as "was routing". But "shadowing off, the engine could not be attributed" is a statement about a failed attribution, not about served capabilities — a session that got that first and later refreshed to an attributed engine serving nothing was told routing had stopped when it had never started The announcement is remembered alongside whether it described actual routing, and only a session that was routing can be told routing stopped

First finding in this run that is wrong as written. Worth recording as such: the fix it prompted is real, but it is not the fix that was asked for, and taking the suggestion at face value would have added a recheck to a path that cannot race while leaving the path that can untouched. The round-12 P2 is also the first regression introduced by a review fix rather than found in the original design.

Round 14 — 1 finding, real

Finding Verdict Fix
P2 — an announcement that never arrived was remembered as said Confirmed, and the suggested remedy could not be applied as written. The line was recorded before publishing, and announce swallowed its own failure, so the call site could not tell a delivered line from a dropped one — "record only after a successful publish" was not expressible until announce reported delivery at all. A toast lost to a briefly unavailable event bridge was remembered as delivered, and every later turn with the same inventory skipped it as unchanged announce reports whether the line reached the session; a failed delivery restores the previous record so the next turn retries. The record is still written before publishing, so a second refresh in the same window cannot send it twice — and restoring the previous value rather than clearing it keeps whether the session had been routing, which a later "routing stopped" depends on

Fifth finding in the class that keeps recurring — the code that describes a decision keeps asserting what used to be true. Here the false assertion was internal: the map claimed the session had been told something it had not.

Both tests were checked against a wrong implementation as well as the real one: the first fails under record-before-publish-and-never-retry, the second under clear-on-failure, which loses the routing state and silently suppresses a later stop. A fix this small has two plausible wrong shapes, and only one of them is the reported defect.

Round 15 — 1 finding, real, and it is a regression in round 14's fix

Finding Verdict Fix
P2 — a failed rollback reinstated a line that was never delivered Confirmed. Round 14 rolled a failed publication back to the record it replaced, but that record was itself optimistic. Two refreshes can publish different lines before either settles; when both failed, the second's rollback reinstated the first's undelivered line as delivered, and a later turn returning to that inventory skipped it as already said Confirmed deliveries and in-flight attempts are separate maps. Nothing reaches the delivered map until the line arrives, so a failure leaves the session's state untouched and the next turn retries. Each attempt is its own object, so identity settles which publication is current — one finishing after a newer one started finds its attempt gone and records nothing

Two rounds in a row on the same eight lines, and the second was caused by the first. Worth being plain about why: round 14 fixed the symptom it was handed — an undelivered line remembered as delivered — by restoring the previous value, without asking whether that value was itself trustworthy. It was not. The bug was never the rollback; it was that one map was being asked to hold two different facts, what the session has been told and what is currently being sent. Splitting them is what actually closes it, and it makes the out-of-order case fall out for free rather than needing its own guard.

Both tests were checked against a wrong implementation as well as the real one: the overlapping-failure test fails under round 14's rollback, and the out-of-order test fails when the identity guard is dropped.

Round 16 — 1 finding, real, and the half of round 13's P1 that was left open

Finding Verdict Fix
P1 — the fallback was pinned by name, not by what the name meant Confirmed, and correctly scoped: round 13's fix closed the case where the identity of the default connection changes mid-flight, and this is the case it did not close. The same name can be re-added against a different warehouse while the call is suspended in the dbt attempt, and Registry.get(name) still consults the mutable registry afterwards, so the call would execute under a decision computed for the connection that name used to mean The routing decision is a function of the connection's canonical type, so pinning the type pins the decision. A replacement whose type differs is refused, naming the connection; a rewrite that keeps the type — including an alias change like postgres to postgresql — cannot change where the call is routed and still runs

This one was known: the scope limit was recorded when round 13's fix landed, as the part needing the verdict bound to the connector rather than the name. Review found it independently, which is the useful outcome — it means the gap was real rather than theoretical, and it arrived with the evidence needed to fix it narrowly. The fix stays inside the handler: no coupling to precedence, no session identity, no enforcement at the connector layer that Decision 9 rejected.

Both tests were checked against a wrong implementation as well as the real one, in both directions: the replacement test fails with no guard at all, and the still-runs test fails when the guard compares config identity instead of canonical type — too strict turns every harmless rewrite into a spurious failure, which is the more likely mistake of the two.

Round 17 — 1 finding, real, and it corrects the shape of rounds 14–16

Finding Verdict Fix
P2 — two successful publications could land out of order Confirmed, but only after checking the premise. Two refresh() calls for one session cannot interleave: resolveTools is awaited inside the step loop, and a second prompt for a running session parks on a callback instead of entering it. What can overlap is the publishing — announce is deliberately not awaited, so a toast can never stall a turn — which means two lines can be in flight across consecutive turns. Landing out of order leaves the stale line on screen while the newer one is recorded as the session's state Publications are chained per session, so arrival order matches decision order and the last line a session sees is the newest

This one is worth reading as a correction to the three rounds before it rather than a fourth bug. The announcement path had accumulated a guard per race: a rollback (14), an identity check (15), a scope pin (16). Each was a correct answer to the finding it was handed. But ordering is a property worth having outright, not a race worth guarding, and once publications are chained the overtaken-publication case cannot occur at all — so the two tests written to guard that race now assert the ordering instead. Fewer moving parts than before the round.

The premise check mattered and I nearly skipped it: the previous round's overlapping-publication finding was accepted and fixed without first establishing that two refreshes could overlap. They cannot. The scenario is reachable anyway — through the un-awaited publish rather than through concurrent refreshes — so the fix stands, but it was reached by luck rather than by evidence, and the mechanism recorded in that round's entry was wrong.

Round 18 — 1 finding, real

Finding Verdict Fix
P2 — an evicted session's announcement could resurrect itself Confirmed. Publishing is not awaited, so a line can still be in flight when its session falls out of the cache; writing the delivery back afterwards recreated an entry eviction had already removed. Eviction only ever walks bySession, so nothing could reclaim it, and the announcement cache would grow with the lifetime session count — the bound the eviction exists to hold The write-back happens only while the session is still tracked

This is the first finding in four rounds on this path that is an invariant violation rather than a race: the suite already asserts these caches are bounded, and this was a way through that assertion. That is the distinction now being applied — a stated invariant that can be broken gets fixed; a marginal race on a path that already has an invariant gets declined with the evidence.

The test asserts the behaviour rather than the size. A size bound alone passed for an incidental reason on the first attempt — the count it produced (283) did not correspond to the defect being described, which is the same trap as a test named for one case that exercises another. Re-derived, a resurrected record suppresses the announcement an evicted session should get when it comes back, and that assertion fails for exactly the right reason.

Round 19 — 1 finding, real, and fixing it removed a condition

Finding Verdict Fix
P2 — a correction back to the delivered line was suppressed Confirmed. Inventory can return to what was already announced while a different line is still being published; the dedupe compared only against the delivered line, so the correction was dropped and the queue delivered the stale line last The comparison is now against the newest line the session is committed to saying — the one still in flight if there is one, otherwise the one it has been told

Reachability is thin: it needs a toast publication to outlive a full turn, and the state self-corrects on the next refresh, so the exposure is one turn of stale guidance rather than an indefinite one. Under the standing rule that would be a candidate to decline. It was fixed instead because the fix removes a condition rather than adding a guard — the two separate comparisons, one against the delivered line and one against the pending line, collapse into a single question with the same answer whenever nothing is pending. A finding that makes the code smaller is worth taking regardless of how marginal the trigger is.

Round 20 — 1 finding, real; last round

Finding Verdict Fix
P2 — routing that stopped before its announcement landed was never reported stopped Confirmed, and it is the other half of round 19. That round made the dedupe compare against the newest line a session is committed to saying, but left the stop decision reading the delivered record alone. With the first routing line still in flight, a refresh serving nothing queued no correction — and the routing line then arrived after routing had already stopped Both questions now consult one record: the announcement still being published if there is one, otherwise the one the session has been told

Taken rather than declined on the same test as round 19: the fix removes the second source of truth instead of adding a guard for the case. It also finishes a change that had been applied to one of the two questions and not the other, which is its own kind of defect — a half-applied invariant reads as deliberate and is harder to spot than an absent one.

Test consolidation

Seven tests retired into three that state the property directly, after the last round.

The attestation block asserted three refused attach outcomes individually alongside a loop over the whole union, so the samples were already subsumed. The loop now carries what they were adding — the refusal reason, which is what the inventory line and the tool descriptions render, so a refusal with the wrong reason is a wrong explanation shown to the user — and covers undefined, which it could not reach before. The two qualifying outcomes became one loop over the other half of the same allowlist. The four reporting surfaces, each asserted against the same unreachable-caller ruleset, now assert together that every surface agrees with the routing decision — which is the actual property, given that three findings in this review were a surface still asserting what had stopped being true.

Every consolidated invariant was checked by breaking what the deleted tests used to catch: making superseded qualify, making one reporting surface claim routing regardless of reachability, and reporting the wrong refusal reason. Each fails the surviving invariant.

Line count barely moves — 1,003 to 1,001 — because an invariant carries more scaffolding and more explanation than the sample it replaces. That is the honest number, and it is not the point: the gain is that a new outcome variant or a new reporting surface is covered by construction rather than by remembering to add a case. Source grew 695 to 748 across the nine rounds of fixes.

Why the later rounds keep landing in the same two places

Thirteen findings reduce to a handful of shapes, and two of them account for six:

  • A guard placed where an early return skips a check that matters (3). The redirect returns early, so anything above it stops running — that took out the hard deny on destructive statements, the explain validators, and the write confirmation in turn. Each fix was correct and each moved the problem rather than removing it, because the ordering was never directly testable: reaching a dbt-sourced target through the live path needs a real dbt project. The decision is now a pure function with its order documented and its own suite.
  • Describing code that keeps asserting what used to be true (3). When the routing decision gained a condition — reachability, then per-capability reachability — the listing, the inventory line and the tool descriptions carried on advertising the old rule. Nothing they did was wrong on its own; they simply repeated a fact that had stopped holding. Every user-facing report now resolves through the same predicate the routing uses.

Both are structural rather than careless, which is why they recurred: the code made them easy. That is also the argument for having kept going — the later rounds were not finding new instances of the first bug, they were finding new classes.

Tally

Twenty-six findings across twenty rounds plus one routed in — twenty-five real and fixed, one refuted as stated. One false positive in twenty-six, and it still surfaced a real defect next door.

Two of the twenty were regressions introduced by review fixes rather than defects in the original design, both in the announcement path and both in consecutive rounds. That is the honest cost of fixing at the point a finding lands: round 12's fix created round 13's P2, and round 14's created round 15's. Each was caught by the next round, which is the argument for running rounds until one is clean rather than stopping when the findings look small.

The yield never decayed. Rounds 7, 8 and 9 were each run to see whether the well had run dry, and each returned something real; round 10 came back clean and rounds 11–20 returned eleven more. Two patterns account for most of it — a guard placed where an early return skips a check that matters (3), and describing code that keeps asserting what used to be true (5, once by staying silent and once by lying to itself). Both are structural rather than careless, which is why they recurred: the code made them easy. That is the argument for having kept going — the later rounds were not finding new instances of the first bug, they were finding new classes.

Two findings were a pair worth naming: a redirect crosses a permission boundary, and the same crossing fails in opposite directions for an agent that allows everything versus one that denies everything unnamed. And one lesson arrives late — round 13's P1 is a reminder that a confident finding still has to be traced to a suspension point before it is believed.

@ralphstodomingo

Copy link
Copy Markdown
Contributor Author

@codex review

@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: 55b64a366f

ℹ️ 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 on lines +343 to +347
if (warehouse) {
const type = canonicalType(Registry.getConfig(warehouse)?.type)
if (!type) return RUN
const entry = precedence.shadowed.get(type)?.get(capability)
return entry ? redirectFor(capability, entry, precedence.workspaceName, warehouse) : RUN

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Preserve analyst access when redirecting warehouse reads

In the default analyst agent, sql_execute, sql_explain, and schema_inspect are explicitly allowed, but unspecified tools are denied (agent.ts:249-268). This branch nevertheless redirects those allowed native calls to datamate_* keys, whose wrapper performs a separate permission check (session/prompt.ts:1866-1872) that therefore denies the call. Consequently, an analyst using any connection shadowed by a workspace can no longer execute the read-only warehouse operations it is intended to use; precedence must account for the destination permission or enforce the native read/write policy on the engine call itself.

Useful? React with 👍 / 👎.

@ralphstodomingo

Copy link
Copy Markdown
Contributor Author

@codex review

@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: 53516d51ce

ℹ️ 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 on lines +536 to +537
const served = CAPABILITIES.filter((c) => byCapability.has(c)).map((c) => c.replace(/^(sql|schema)_/, ""))
const local = CAPABILITIES.filter((c) => !byCapability.has(c)).map((c) => c.replace(/^(sql|schema)_/, ""))

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Honor destination permissions in warehouse annotations

When the active ruleset denies an engine key—as the default analyst agent does—check() deliberately runs that capability locally via reachable(), but this function still classifies every materialized capability as served by the workspace. Consequently, warehouse_list reports rows such as “execute via workspace” (and increments shadowed) even though the same agent's execution will stay local; filter each capability through the captured ruleset, matching the routing decision.

Useful? React with 👍 / 👎.

@ralphstodomingo

Copy link
Copy Markdown
Contributor Author

@codex review

@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: ed319f1e42

ℹ️ 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 on lines +185 to +187
const cfg = (await Config.get()) as { mcp?: Record<string, ExistingEntry | undefined> }
const entry = cfg.mcp?.[DATAMATE_KEY]
if (!entry) return null

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Verify the connected engine rather than its saved config

When a running server's datamate command is edited from an unpinned or wrong-workspace entry to one pinned to the current binding, Config.get() returns the new argv while MCP can continue serving the already-connected process. This then attributes that old client to the new workspace and redirects warehouse queries to an engine that may still serve another teammate; derive attribution from the runtime client/attach outcome or reconnect it before enabling precedence.

Useful? React with 👍 / 👎.


/** Per-session precedence, refreshed once per turn by the tool resolver and read
* (never recomputed) by tool bodies mid-turn. */
const bySession = new Map<string, Precedence>()

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Bound the per-session precedence caches

In a long-running serve process that handles many distinct session IDs, every refresh permanently adds an entry to bySession, and sessions that emit an inventory line are also retained in announced; neither cache is evicted or cleared when a session is deleted. This makes memory grow with the lifetime session count and retains each session's merged permission ruleset, so cap or evict both collections as the neighboring workspace session caches do.

Useful? React with 👍 / 👎.

@ralphstodomingo
ralphstodomingo force-pushed the feat/workspace-precedence branch from 7fef85c to 19cb5e5 Compare August 26, 2026 20:07
@ralphstodomingo

Copy link
Copy Markdown
Contributor Author

@codex review

@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: 19cb5e5f17

ℹ️ 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 on lines +165 to +167
const outcome = precedenceInternals.attachOutcome
? await precedenceInternals.attachOutcome()
: await ensure(sessionID)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Preserve the capped engine-attach wait

When the initial attach exceeds the 15-second whenAttached budget, or a repairable attach retry is still in flight, awaiting ensure(sessionID) here blocks resolveTools on the uncapped attach task. This defeats the timeout in session/prompt.ts:1027-1030 and the non-blocking retry contract in engine-sync.ts:880-918, so a broken MCP connection can delay every affected prompt for its full connection timeout rather than letting the turn proceed without workspace tools; attest only an already-settled outcome or use the same bounded wait.

Useful? React with 👍 / 👎.

Two ways a stale or absent decision could route a call wrongly.

Re-linking mid-session is supported, so the turn's snapshot can name a workspace
the project has already left. A redirect naming it would send the query to that
workspace's engine, with its credentials — the mis-routing this design exists to
prevent, arriving through the passage of time rather than a bad read. The binding
is now re-checked before any redirect; it is a local cache read, and only the path
about to redirect pays for it.

A session with no snapshot returned a bare "run". That is indistinguishable from
a considered "this connection is not served", so an evicted entry — possible
between tool resolution and the call — would let a shadowed connection execute
locally with no indication. Unknown now runs locally and says so, like every other
undetermined case here.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RM9xasTNbk2k1eqhpF1Hp5
@ralphstodomingo

Copy link
Copy Markdown
Contributor Author

@codex review

@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: 5de539e645

ℹ️ 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 on lines +284 to +287
const line = inventoryLine(result)
if (line && announced.get(sessionID) !== line) {
announced.set(sessionID, line)
void announce(line).catch(() => {})

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Announce when the routing inventory becomes empty

When an engine previously materialized routing tools and later removes all of them, inventoryLine() changes from the announced routing summary to an empty string for nothing-materialised, but this truthiness guard suppresses the update and leaves the user believing calls are still routed while they now run locally. Fresh evidence after the earlier inventory review is that comparing the last line fixes partial capability changes but still cannot announce the all-capabilities-removed transition; emit an explicit shadowing-off correction for this state.

Useful? React with 👍 / 👎.

Comparing the last announced inventory catches a capability appearing or
disappearing, but not routing ending altogether: with nothing left to enumerate
the line is empty, and an empty line was treated as nothing to say. So a session
told its calls were routed through the workspace kept believing that after the
engine stopped serving anything, while the calls ran locally.

That transition is the one a user most needs. It is now stated explicitly, and
only to a session that had previously been told otherwise — a session that never
had routing is still told nothing, because for it nothing changed.

Third finding in the same class: the code that describes a decision keeps
asserting what used to be true. Twice that was a stale claim; this time it was
silence, which is the same failure wearing different clothes.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RM9xasTNbk2k1eqhpF1Hp5
@ralphstodomingo

Copy link
Copy Markdown
Contributor Author

@codex review

@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: 2a017abac2

ℹ️ 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 on lines +505 to +506
if (warehouse) {
const type = canonicalType(Registry.getConfig(warehouse)?.type)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Recheck connections after concurrent registry updates

When a warehouse call overlaps a parallel warehouse_add that replaces the same name, this lookup can classify the old connection as unserved and return RUN, while the handler later calls Registry.get(warehouseName) and executes the newly replaced, workspace-shadowed connection locally. The analogous race affects warehouse-less calls when concurrent add/remove operations change the first registry entry after default-target resolution. Because tool calls can execute concurrently, this can bypass workspace routing and its audit path; bind the verdict to the connector/config actually selected for execution or repeat precedence at that selection point.

Useful? React with 👍 / 👎.

Comment on lines +295 to +296
const previous = announced.get(sessionID)
const line = inventoryLine(result) || (previous ? STOPPED_ROUTING : "")

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Track whether the previous announcement represented routing

When the first refresh is unattributed, inventoryLine records a nonempty “shadowing off” announcement; if a later refresh becomes attributed but materializes no mapped or permitted capability, its inventory is empty and this truthiness check emits “nothing is served ... any more.” No routing ever occurred in that sequence, which is common when the initial attach exceeds its bounded wait and eventually exposes only non-warehouse tools. Track whether the previous state actually had served capabilities instead of treating every prior announcement as active routing.

Useful? React with 👍 / 👎.

ralphstodomingo added 2 commits August 27, 2026 05:52
`refresh()` remembered the last announcement as a bare string, so any
non-empty prior line counted as "was routing". An announcement can be
non-routing — "shadowing off, the engine could not be attributed" is a
statement about a failed attribution, not about served capabilities.

A session that got that line first and later refreshed to an attributed
engine exposing no warehouse tools was told routing had "stopped", when
it had never started. Track the announcement alongside whether it
described actual routing, and gate the stopped-routing line on that.

Found by automated review.
`sql.execute` resolved its default connection twice: once when the caller's
routing decision was made, and again after `tryExecuteViaDbt` had awaited.
The connection registry is a process-wide mutable singleton, so a
`warehouse.add`/`remove` landing during that await changed which connection
the call fell back to — after the decision had been made against the old one.

Read the registry once, before the await. The decided and executed
connections are now the same by construction, and a call whose connection
disappears mid-flight reports that connection by name instead of quietly
running somewhere else. The dbt-first ordering is unchanged.

Found by automated review.
@ralphstodomingo

Copy link
Copy Markdown
Contributor Author

@codex review

@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: 73034ac942

ℹ️ 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 on lines +304 to +306
if (line && previous?.line !== line) {
announced.set(sessionID, { line, routed })
void announce(line).catch(() => {})

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Record announcements only after successful delivery

When publishing the toast fails—for example, while the event bridge is temporarily unavailable—announce() catches the error, but this code has already recorded the line as delivered. Every later refresh with the same inventory then skips it because previous.line === line, so the session never receives the routing disclosure despite the failure being recoverable; update announced only after a successful publish, or retain a pending state that allows retries.

Useful? React with 👍 / 👎.

The inventory line was recorded as said before it was published, and
`announce` swallowed its own failure, so the call site could not tell a
delivered line from a dropped one. A toast lost to a briefly unavailable
event bridge was therefore remembered as delivered — and every later turn
with the same inventory skipped it as unchanged, so the session was never
told what its calls were doing.

`announce` now reports whether the line reached the session, and a failed
delivery restores the previous record so the next turn retries. The record
is still written before publishing, so a second refresh in the same window
does not send the line twice; restoring the previous value rather than
clearing it keeps whether the session had been routing, which a later
"routing stopped" depends on.

Found by automated review.
@ralphstodomingo

Copy link
Copy Markdown
Contributor Author

@codex review

@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: 3468a36da1

ℹ️ 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 on lines +319 to +321
if (announced.get(sessionID)?.line !== line) return
if (previous) announced.set(sessionID, previous)
else announced.delete(sessionID)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Retry both failed overlapping announcements

When two consecutive refresh() calls publish different inventories before either publication settles, and both publications fail, the first failure leaves the newer optimistic record untouched while the second failure restores previous, even though that earlier line was never delivered either. A later refresh returning to that inventory then skips it as already announced, permanently losing the routing disclosure. Fresh evidence beyond the earlier delivery-failure review is this overlapping-failure rollback path; track confirmed delivery separately rather than restoring an optimistic predecessor.

Useful? React with 👍 / 👎.

The previous fix rolled a failed publication back to the record it had
replaced — but that record was itself optimistic. Precedence is re-derived
every turn, so two refreshes can publish different lines before either
settles; when both failed, the second's rollback reinstated the first's
undelivered line as delivered, and a later turn returning to that inventory
skipped it as already said. The disclosure was lost for good.

Confirmed deliveries and in-flight attempts are now separate. Nothing
reaches the delivered map until the line actually arrives, so a failure
leaves the session's known state untouched and the next turn retries. The
in-flight record exists only to stop the same line being sent twice in one
window, and each attempt is its own object, so identity settles which
publication is current — one that finishes after a newer one started finds
its attempt gone and records nothing.

Found by automated review.
@ralphstodomingo

Copy link
Copy Markdown
Contributor Author

@codex review

@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: 90e4877933

ℹ️ 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".

"No warehouse configured. Use warehouse.add, set ALTIMATE_CODE_CONN_* env vars, or configure a dbt profile.",
)
}
const connector = await Registry.get(fallbackName)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Pin the selected connector, not only its name

When a concurrent warehouse_add replaces the same connection name after precedence checks its old, unshadowed config, this lookup resolves the replacement config and can execute a now-workspace-shadowed connection locally, bypassing workspace routing and auditing. Fresh evidence after the prior registry-race finding is that the attempted fix pins only the fallbackName string; Registry.get(fallbackName) still consults the mutable registry after the dbt await. Bind the actual selected config/connector or repeat precedence against the connector selected here.

Useful? React with 👍 / 👎.

Pinning the name closed the case where the identity of the default
connection changed mid-flight, but not a same-name replacement: the name
can be re-added against a different warehouse while the call is suspended
in the dbt attempt, and `Registry.get(name)` still consults the mutable
registry afterwards. The call would then execute under a routing decision
computed for the connection that name used to mean.

The decision is a function of the connection's canonical type, so pinning
the type pins the decision. A replacement whose type differs is refused
with a message naming the connection, rather than run under a verdict that
never covered it; a rewrite that keeps the type — including one that only
changes an alias, `postgres` to `postgresql` — cannot change where the call
is routed and still runs.

Found by automated review.
@ralphstodomingo

Copy link
Copy Markdown
Contributor Author

@codex review

@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: ebfc668499

ℹ️ 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 on lines +330 to +333
void announce(line).then((delivered) => {
if (publishing.get(sessionID) !== attempt) return
publishing.delete(sessionID)
if (delivered) announced.set(sessionID, attempt)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Serialize routing announcement delivery

When two refresh() calls publish different inventories concurrently and both succeed out of order, the newer line can be delivered first and recorded in announced, then the older publication can arrive last but be ignored by this identity check. The user is left seeing the stale inventory, while a later refresh for the newer state is suppressed because it is already recorded as delivered; serialize publications per session or re-publish the current inventory after an overtaken delivery settles.

Useful? React with 👍 / 👎.

Refreshes are serialized by the prompt loop, but publishing is deliberately
not awaited — a toast must never be able to stall a turn — so two lines
could be in flight at once. Landing out of order left the stale line on
screen while the newer one was recorded as the session's state, and the
identity guard that decided which publication was current only made the
older one silent rather than correct.

Publications are now chained per session, so arrival order matches decision
order and the last line a session sees is the newest one. Ordering is a
property worth having outright rather than a race worth guarding: with the
chain in place an overtaken publication cannot occur, so the two tests that
guarded that race now assert the ordering itself.

Found by automated review.
@ralphstodomingo

Copy link
Copy Markdown
Contributor Author

@codex review

@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: bf9f35bb02

ℹ️ 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 on lines +339 to +341
const delivered = await announce(line)
if (publishing.get(sessionID) === attempt) publishing.delete(sessionID)
if (delivered) announced.set(sessionID, attempt)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Prevent evicted announcements from repopulating the cache

When a toast publication is still pending while more than 256 sessions cause this session to be evicted, remember() removes its announced entry, but the pending callback later executes this unconditional announced.set() and resurrects it. Repeating this with slow event delivery leaves entries whose session IDs are no longer present in bySession, so subsequent cap enforcement can never select them and announced again grows with lifetime session count. Fresh evidence beyond the earlier cache-bound review is this post-eviction asynchronous write; record delivery only if the session/attempt is still tracked, or apply eviction again after completion.

Useful? React with 👍 / 👎.

Publishing is not awaited, so a line can still be in flight when its session
falls out of the cache. Writing the delivery back afterwards recreated an
entry for a session eviction had already removed — and eviction only ever
walks `bySession`, so nothing could reclaim it. The announcement cache would
then grow with the lifetime session count, which is the bound the eviction
exists to hold.

The write-back now happens only while the session is still tracked. The test
asserts the behaviour rather than the size alone: a resurrected record would
suppress the announcement an evicted session gets when it is derived again,
which is the visible consequence and a sharper signal than the count.

Found by automated review.
@ralphstodomingo

Copy link
Copy Markdown
Contributor Author

@codex review

@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: babb2c5c95

ℹ️ 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".

const routed = result.enabled && current !== ""
// Only a session that was actually routing can be told routing has stopped.
const line = current || (previous?.routed ? STOPPED_ROUTING : "")
if (line && previous?.line !== line && publishing.get(sessionID)?.line !== line) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Requeue a reverted inventory while another line is pending

When consecutive turns change a session from an already-delivered inventory A to B and back to A before B's asynchronous publication completes, this condition suppresses the corrective A because previous.line is still A. The serialized queue then delivers B last and records it as announced even though the current routing state is A, leaving the user with stale routing guidance until another turn happens to refresh it. Fresh evidence beyond the earlier publication-order finding is that the new queue orders delivery but the deduplication still compares against the last delivered line rather than the last queued target; compare against the pending target/current state or enqueue a correction after B settles.

Useful? React with 👍 / 👎.

Inventory can return to what was already announced while a different line is
still being published. Comparing only against the delivered line suppressed
that correction, and the queue then delivered the stale line last — leaving
the session looking at routing guidance that no longer matched where its
calls went.

The comparison is now against the newest line the session is committed to
saying: the one still in flight if there is one, otherwise the one it has
actually been told. That replaces the two separate guards — one against the
delivered line, one against the pending line — with a single question, and
it is the same answer as before whenever nothing is pending.

Found by automated review.
@ralphstodomingo

Copy link
Copy Markdown
Contributor Author

@codex review

@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: 6d82419a56

ℹ️ 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".

const current = inventoryLine(result)
const routed = result.enabled && current !== ""
// Only a session that was actually routing can be told routing has stopped.
const line = current || (previous?.routed ? STOPPED_ROUTING : "")

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Queue a stop notice when pending routing disappears

When the first routing announcement is still pending and a subsequent refresh removes all materialized capabilities, previous is still unset, so this produces an empty line and queues no correction. The original routing announcement can then arrive after routing has already stopped, leaving the user with stale guidance until another turn refreshes the session. Fresh evidence beyond the earlier empty-inventory and queue findings is that the stop decision consults only the last delivered record rather than publishing.get(sessionID)?.routed; pending routed announcements must also trigger a queued stop notice.

Useful? React with 👍 / 👎.

ralphstodomingo added 2 commits August 27, 2026 07:12
The previous change made the dedupe compare against the newest line a
session is committed to saying, but left the stop decision reading the
delivered record alone. With a first routing announcement still in flight, a
refresh that served nothing therefore queued no correction — and the routing
line arrived after routing had already stopped.

Both questions now consult the same record: the announcement still being
published if there is one, otherwise the one the session has been told. That
removes the second source of truth rather than adding a guard for the case,
and it finishes a change that was only half applied.

Found by automated review.
Seven tests retired into three that state the property directly.

The attestation block asserted three refused attach outcomes individually
alongside a loop over the whole union; the samples were subsumed. The loop
now carries what they were adding — the refusal reason, which is what the
inventory line and tool descriptions render, so a refusal with the wrong
reason is a wrong explanation shown to the user — and covers `undefined`,
which it could not reach before. The two qualifying outcomes merge into one
loop over the other half of the same allowlist.

The four reporting surfaces were asserted one test each against the same
unreachable-caller ruleset. The failure they guard is precisely that these
drift apart, so they now assert together that every surface agrees with the
routing decision.

Line count barely moves: an invariant carries more scaffolding than the
sample it replaces. The gain is that a new outcome variant or a new
reporting surface is covered by construction rather than by remembering to
add a case.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant