Software factory change - #516
agent-relay-code[bot] wants to merge 2 commits into
Conversation
…talog f.gitlab carries comments and discussions; f.github carries issues, pull requests, reviews, refs, merge and close-pull-request. The catalog marked both `supported: true`, so the only way to learn the difference was to reach for `f.gitlab.issues` and read `undefined is not a function` — or to dump the writeback catalog before writing a line. A GitLab-sourced flow shelled out to `glab` for every read as a result. Parity is upstream work. What is fixed here is the silence: `supported` now distinguishes `'partial'` from full, the generator carries the resources each provider actually dispatches and the note that says what a partial omits, and every layer an author can reach the gap through refuses by name — `flows check` statically, the surface's property guard for a computed name, both worded by one function and neither touching the provider to refuse. Co-Authored-By: Claude <noreply@anthropic.com>
|
Important Review skippedBot user detected. To trigger a single review, invoke the ⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Advanced Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
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. Comment |
…ent on code it never read
The refusal for a member `f.gitlab` does not carry was reaching for surface
exports that the pinned, published surface does not ship, and was reading
text that is not a member access as one.
- The SDK no longer imports `unsupportedHelperMemberMessage` or
`UnsupportedHelperMemberError`. Both are unreleased, and this source is
installed against a published surface in the schema `validate` job, where a
missing named export fails the whole module at load — before preflight can
run. The wording is restated locally and pinned equal to the surface's in
test; the envelope remap matches `error.name`, which is also correct across
the realm boundary an authored flow file's own surface copy creates.
- Static inspection now admits every member the runtime guard still resolves.
The guard refuses only what the bound object lacks and is neither `then` nor
`toJSON`, so `f.gitlab.hasOwnProperty('comments')` returns `true` at run
time; `flows check` must not reject feature detection that works.
- A regular-expression literal is blanked with the other data literals, so
`/f.gitlab.issues/.test(line)` — which inspects text and reaches no helper —
no longer refuses. Ambiguous `/` resolves to "regex", which can only
withdraw a static refusal and leave the runtime guard to make it.
- A body that binds the context parameter's name again is left to the runtime
guard. Renaming a local callback parameter cannot decide whether a flow is
admitted, and an inner `f` need not be the flow context at all.
The last three can only withdraw refusals, never invent them; the runtime
guard remains the backstop for everything they decline to judge.
Co-Authored-By: Claude <noreply@anthropic.com>
|
Relayflow: the adversarial review did not pass. This branch is not approved: the flow stopped here and did not mark it ready to merge. Review of PR #516Reviewed head: Changes requested. 1. [P1] Compile the SDK against its pinned published surfaceLocation: Removing the new named imports fixes the module-load failure, but the SDK still cannot compile against its declared dependency, I copied the current SDK source, package.json and tsconfig.json into Ship and pin compatible surface types, or explicitly normalize the old/new catalog shapes before reading the optional new fields. Add coverage that compiles with the published dependency rather than only the local surface. The now-green schema validation job runs Bun source tests and does not demonstrate this TypeScript build succeeds. 2. [P2] Do not refuse accesses on destructured locals or method parametersLocation: The rebinding detector handles simple declarations and function/arrow/catch parameters, but misses destructured declarations and method parameters. Both of these valid bodies return 42 without accessing a GitLab helper: (f) => { { const { f } = { f: { gitlab: { issues: 42 } } }; return f.gitlab.issues; } }
(f) => ({ read(f) { return f.gitlab.issues; } }).read({ gitlab: { issues: 42 } })With the GitLab mount fact satisfied, preflight rejects both as Scope and limitsReviewed all 16 changed files, the previous local review, and the PR discussion, inline comments and submitted reviews via paginated GitHub API requests. At the captured snapshot there is one CodeRabbit skipped-review notice, no inline comments, and no submitted reviews. The PR description still describes the original commit's scanner and tests; it is not evidence for the follow-up fixes. The follow-up covers the earlier inherited-member and simple regex reproductions with regression tests. The package compatibility problem persists at compilation, and shadowing remains incomplete as described above. No production source, generated file, test gate, or docs/evidence file was changed during this review. No live GitLab API verification is claimed. No PR comments were posted. Affected-package verification
Reproduction sourceThe probe executes harmless data-only bodies and calls preflight with a satisfied mount; no provider is contacted. import { preflightHelpers } from '/home/daytona/.relayflow-v2-supervisor/durable/repository/packages/sdk/src/helper-preflight.ts';
import { createHelpers } from '/home/daytona/.relayflow-v2-supervisor/durable/repository/packages/surface/dist/runtime.js';
const facts = {providers:{gitlab:{mount:true,mock:false}}};
const cases = {
destructured: '(f) => { { const { f } = { f: {gitlab:{issues:42}} }; return f.gitlab.issues; } }',
method: '(f) => ({ read(f) { return f.gitlab.issues; } }).read({gitlab:{issues:42}})',
namespacePrefix: '(f) => { f.gitlabExtra = {issues:42}; return f.gitlabExtra.issues; }',
regexAfterReturnNewline: '(f) => { return\n /f.gitlab.issues/.test("x"); }',
};
const helpers = createHelpers(() => {throw Error('unexpected dispatch')});
for (const [name,source] of Object.entries(cases)) {
try {const body = new Function(`return ${source}`)(); console.log(JSON.stringify({name, runtime:body({...helpers}), preflight:preflightHelpers({body},facts)}));} catch(e) { console.log(name,String(e));}
}Captured commandbun /tmp/gitlab-review-head/probe.tsExit code: 0. Captured stdout/stderr: Captured commandpackages/sdk/node_modules/.bin/tsc --noEmit -p /tmp/gitlab-review-head/clean-sdk/tsconfig.jsonExit code: 2. Captured stdout/stderr: Captured commandgit show origin/main:packages/sdk/src/helper-preflight.ts > /tmp/gitlab-review-head/clean-sdk/src/helper-preflight.ts
packages/sdk/node_modules/.bin/tsc --noEmit -p /tmp/gitlab-review-head/clean-sdk/tsconfig.jsonExit code: 0. Captured stdout/stderr (empty): Captured commandnpm test --prefix packages/surfaceExit code: 0. Captured stdout/stderr: Captured commandnpm run typecheck:regressions --prefix packages/surfaceExit code: 0. Captured stdout/stderr: Captured commandgh pr checks 516Exit code: 0. Captured stdout/stderr: Captured commandgh api --paginate repos/AgentWorkforce/flows/issues/516/commentsExit code: 0. Captured stdout/stderr: Captured commandgh api --paginate repos/AgentWorkforce/flows/pulls/516/commentsExit code: 0. Captured stdout/stderr: Captured commandgh api --paginate repos/AgentWorkforce/flows/pulls/516/reviewsExit code: 0. Captured stdout/stderr: Reproducing the dependency-isolated TypeScript checkThe temporary SDK keeps all other installed dependencies fixed and changes only surface resolution from the local symlink to the registry artifact. From the repository root, use a fresh temporary directory (the path below was used for the captured run): npm pack @relayflows/surface@2.0.22 --pack-destination /tmp/gitlab-review-head/registryThe source-copy and dependency setup used for the captured check: from pathlib import Path
import shutil, tarfile
repo=Path.cwd(); target=Path('/tmp/gitlab-review-head/clean-sdk'); target.mkdir(exist_ok=True)
shutil.copytree(repo/'packages/sdk/src',target/'src',dirs_exist_ok=True)
shutil.copy(repo/'packages/sdk/package.json',target/'package.json')
shutil.copy(repo/'packages/sdk/tsconfig.json',target/'tsconfig.json')
deps=target/'node_modules'; deps.mkdir(exist_ok=True)
for p in (repo/'packages/sdk/node_modules').iterdir():
if p.name=='@relayflows':
(deps/p.name).mkdir(exist_ok=True)
for child in p.iterdir():
if child.name!='surface': (deps/p.name/child.name).symlink_to(child.resolve())
else: (deps/p.name).symlink_to(p.resolve())
surface=deps/'@relayflows/surface'; surface.mkdir(exist_ok=True)
with tarfile.open('/tmp/gitlab-review-head/registry/relayflows-surface-2.0.22.tgz') as tar:
for m in tar.getmembers():
if m.name.startswith('package/'):
m.name=m.name[len('package/'):]; tar.extract(m,surface,filter='data')Registry pack command captured output (exit 0): Captured full SDK test commandnpm test --prefix packages/sdkExit code: 1. Full captured stdout/stderr: |
Name the gap where
f.gitlab's namespace outruns its writeback catalogThe problem
f.githubcarriesissues,pull-requests,reviews,refs,merge,close-pull-request,issue-comments,replies.f.gitlabcarriescommentsanddiscussions. The provider catalog marked bothsupported: true, so the difference was discoverable in exactly two ways:reach for
f.gitlab.issuesand readundefined is not a function, or dump thewriteback catalog before writing a line of flow. A GitLab-sourced flow shelled
out to
glabfor every read as a result.Parity on
issues/merge_requestsis upstream@relayfile/relay-helperswork. This PR takes the second acceptable resolution from the ticket: make the
gap visible everywhere an author can reach it.
What changed
The catalog distinguishes partial from full (
scripts/generate-helpers.mjs→ regenerated
providers.ts).supportedis nowtrue | 'partial' | false,and every entry carries the sorted
resourcesit actually dispatches. APARTIAL_SUPPORTmap in the generator holds the note, so a later upstreamrelease has to revisit it deliberately — a larger resource count is not by
itself a promotion. GitLab:
{ "provider": "gitlab", "namespace": "gitlab", "mockEnv": "RELAYFLOWS_GITLAB_MOCK", "supported": "partial", "resources": ["comments", "discussions"], "note": "Comments and discussions only. Issue list/read/create and merge-request list/read/create are unavailable through f.gitlab." }supported: trueis not newly redefined; it always meant "this client existsand its resources dispatch". Both the generated header comment and
HelperSupportnow say so, so the next reader does not have to infer it.One refusal, worded once (
packages/surface/src/helper-support.ts).unsupportedHelperMemberMessagebuilds the message;UnsupportedHelperMemberErrorcarries
provider,member,resourceandavailablestructurally.Preflight and the runtime guard both call it, so they cannot drift apart:
Three layers refuse, none of them by touching the provider.
GitlabHelper;f.gitlab.issuesis already a type error.flows checkhelper-preflight.tsstatically readsf.gitlab.<member>off the body's own context parameter and refuseshelper_provider.unsupported— before the mount question and regardless of mock mode, because installing a mount cannot conjure a route no client carries.bindHelperwraps a partial provider in a property guard; a computed name (f.gitlab[resource]) refuses at the call site.executeAuthoredFlowmaps it toAuthoredFlowExecutionError('helper_provider.unsupported', …), preserving the surface's message and rethrowing every unrelated failure untouched.invokeHelperrepeats the refusal for an authored envelope that never touched the helper.The guard is a
Proxygettrap, so dot, bracket and aliased access all gothrough it while ordinary object behavior is preserved:
then(the helperstays awaitable),
toJSON, symbols, inheritedObject.prototypemethods, keyenumeration and spread.
pathremains a synchronous path builder, is neverdispatched, and is never advertised as an available verb.
Static inspection stays honest (
packages/sdk/src/source-scan.ts). Thebounded scanner that
flow-requirements.tsalready used is now shared, plus acodeOnlythat blanks comments and string data at unchanged offsets so af.gitlab.issueswritten inside a comment or a string cannot refuse a correctflow. One literal is deliberately kept: an identifier-shaped quoted string
between
[and]is a property name, not data — otherwisef['gitlab']['issues']would read as nothing at all. A computed name is notdecided here; it falls through to the runtime guard.
Deliberately unchanged: the YAML helper catalog (
yaml-helpers.tshas its ownclosed catalog and its own
invokeHelper), triggers, other providers'support values, and non-partial providers' existing generic
invokeHelperdiagnostics.
Tests
packages/surface/tests/helper-support.test.ts(9) — catalog shape across all50 providers (three designations only;
noteiff partial;resourcessorted;false⇒ empty); dot/bracket/aliased refusal; structural fields; wordingshared with preflight;
then/toJSON/symbols/spread/enumeration preserved;pathstill synchronous andcomments.writestill dispatching unchanged;f.github.nonexistentstill plainundefined;invokeHelperrefusing againsta transport that throws on any I/O.
packages/sdk/tests/helper-partial-support.test.ts(8) — preflight refusal andits exact message; bracket, spaced, renamed-parameter and
functionbodies;refusal ordered before
mount_requiredand unaffected by mock mode;comments/discussionsand all off.githubaccepted; a comment, a stringand a template that look like accesses not refused; a computed name reaching
the runtime guard as
helper_provider.unsupported; an unrelatedTypeErrorpassing through untouched;
flows check --jsonon a.flow.tsfixtureexiting 2 with the refusal.
packages/sdk/tests/helpers-fanout.test.ts— the per-provider assertiontoBe(provider.supported)becametoBe(provider.supported !== false): apartial passes the mount question like any other, since a tools-only header
has no body to refuse against.
packages/surface/tests/helpers-typecheck-fail.test-d.ts— two negativeGitLab cases.
Evidence
Not run here: the full
npm test --prefix packages/sdk. Itstest:prepbuildsthe Rust kernel and this environment has no
cargo, so the kernel-backedsuites (
authored-helpers.test.tsand the rest of the daemon tests) were notexercised. The SDK suites above are the ones this change can affect that run
without a kernel; CI covers the remainder.
npm run typecheck:examples --prefix packages/surfacefails onworkflows/stuck-run-triage.flow.ts(77,12): error TS2304: Cannot find name 'URL'.Verified pre-existing: the identical two errors reproduce with this branch's
changes stashed.
Note
Medium Risk
Changes authored-flow preflight, runtime helper binding, and generated provider metadata; incorrect static scanning could false-refuse or miss bad access, though behavior is heavily tested and scoped mainly to partial providers like GitLab.
Overview
Partial helper catalog and clear refusals when a flow reaches for provider resources that do not exist in the writeback client (notably
f.gitlab, which is now'partial'with onlycommentsanddiscussions).The generated provider catalog gains
supported: true | 'partial' | false, sortedresources, and optionalnoteentries. Partial helpers are wrapped at bind time with a property guard that throwsUnsupportedHelperMemberErrornaming available members;invokeHelperuses the same wording for journal envelopes without touching provider I/O.SDK static and runtime alignment:
flows checkscans flow bodies (via sharedsource-scan.ts/codeOnly) for evidentf.<namespace>.<member>access on partial providers and refuseshelper_provider.unsupportedbefore mount/mock checks.executeAuthoredFlowremaps that runtime error to the same preflight code byerror.name, preserving unrelated body failures.Docs, generator
PARTIAL_SUPPORT, and tests cover editor types, preflight, runtime guard, and CLIcheck.Reviewed by Cursor Bugbot for commit 889198d. Bugbot is set up for automated code reviews on this repo. Configure here.
Summary by cubic
Marks
f.gitlabas partially supported in the provider catalog so reaching for resources it does not carry (issues, merge requests) now fails with a clear message naming what is available, instead ofundefined is not a function.Catalog
supportednow distinguishestrue,'partial', andfalse; every provider entry lists theresourcesit dispatches, and partial entries add anote.PARTIAL_SUPPORTmap, so a later upstream release has to promotef.gitlabdeliberately.Refusal layers
flows checkstatically refuses evidentf.gitlab.<missing>access before the mount question, regardless of mock mode; the generatedGitlabHelperJSDoc makes missing members a type error in the editor.Proxyguard on the bound helper refuses dot, bracket, and computed member access at runtime without touching the provider.name, so a published surface without the new exports cannot break preflight; a test pins the wordings equal.Written for commit 889198d. Summary will update on new commits.