Repository navigation
feat(accounts,saas-ui): a bucket source takes include and exclude key patterns, applied before any fetch - #1216
Closed
antoinetoussaint-byte wants to merge 2 commits into
Closed
antoinetoussaint-byte wants to merge 2 commits into
antoinetoussaint-byte wants to merge 2 commits into
Conversation
… patterns, applied before any fetch UploadDatasourceConfig gains include_patterns and exclude_patterns (bounded RE2 over the key relative to the prefix; exclude wins; empty include admits all) and file_extensions with GitHub's semantics. AddSource and UpdateSource refuse an invalid pattern by field (INVALID_ARGUMENT, a BadRequest violation, and a message leading with the field path), never at sync. The store's listing applies the filter before it counts against max_objects, so an excluded object is never fetched, and the closing listing names only matches: narrowing the filter through UpdateSource tombstones what it now excludes on the next sync. The kit's object-storage connect form, with its shared collection field, moves here from the catalog change and takes the key filter, placing a host refusal under the field it names. Refs #1211 Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This was referenced Oct 21, 2026
…Scope, not the listing client's The owner's review of #1216: the match sat inside objectstore/client.go's List, the file #1211 deletes, so it would have had to move again when the object-storage service replaces the client. objectstore.Scope now holds the host's InScope predicate (uploadKeyScope, as GitHub's SourceConfig.InScope) and the object cap, and Window applies both to whatever listed the prefix: an excluded key is dropped before it is counted, never fetched, and a cut is reported. client.go returns the store's page whole; its cap moved into Scope with the filter. UploadContentClient.List is back to main's shape. The kit moves to 0.12.16 (saas-ui, and ui with it) and the SDK to 0.3.36, past what v0.0.105 released; saas-ui's SDK floor rises to ^0.3.36 because the form sends fields only that SDK carries. package-lock.json is regenerated by npm. Refs #1211 Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This was referenced Oct 23, 2026
Contributor
Author
|
Closing as finished work with no remaining home: #1235 contains this branch's head commit outright.
So this needs no rebase and no review. Rebasing it onto main — it is 22 commits behind — would re-run a full strict-main CI cycle on commits that already sit in a newer, green PR. #1235 is the one self-contained PR for issue #1211 and is where the review is happening. Reopen if that compare ever stops being true. 🤖 Generated with Claude Code |
crisfiorini-oss
pushed a commit
to crisfiorini-oss/module-saas-starter
that referenced
this pull request
Oct 27, 2026
…ct-storage service; the host's store client is deleted (codefly-dev#1235) * feat(accounts): the object-storage connector diffs the current listing against one the host retains An object store answers only "what is there now". objectstore.FilesConnector versions a source by the digest of its listing (key, ETag, size) under its binding, the host retains the listing each version names in datasource_object_manifests (migration 46, tenant-owned, newest four per source), and Changes(from) diffs against it. It reaches the store through a Bucket interface the object-storage service's per-call binding will implement (codefly-dev/service-object-storage#108), and passes connectortest.RunFiles over a scripted bucket. It is not yet what the host syncs through: the provider stays registered non-conformant and runUploadSync keeps running. The upload descriptor now states source-scoped readers in the catalog. Refs codefly-dev#1211 Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * feat(accounts,saas-ui): a bucket source takes include and exclude key patterns, applied before any fetch UploadDatasourceConfig gains include_patterns and exclude_patterns (bounded RE2 over the key relative to the prefix; exclude wins; empty include admits all) and file_extensions with GitHub's semantics. AddSource and UpdateSource refuse an invalid pattern by field (INVALID_ARGUMENT, a BadRequest violation, and a message leading with the field path), never at sync. The store's listing applies the filter before it counts against max_objects, so an excluded object is never fetched, and the closing listing names only matches: narrowing the filter through UpdateSource tombstones what it now excludes on the next sync. The kit's object-storage connect form, with its shared collection field, moves here from the catalog change and takes the key filter, placing a host refusal under the field it names. Refs codefly-dev#1211 Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * refactor(accounts,saas-ui): the bucket key filter is the connector's Scope, not the listing client's The owner's review of codefly-dev#1216: the match sat inside objectstore/client.go's List, the file codefly-dev#1211 deletes, so it would have had to move again when the object-storage service replaces the client. objectstore.Scope now holds the host's InScope predicate (uploadKeyScope, as GitHub's SourceConfig.InScope) and the object cap, and Window applies both to whatever listed the prefix: an excluded key is dropped before it is counted, never fetched, and a cut is reported. client.go returns the store's page whole; its cap moved into Scope with the filter. UploadContentClient.List is back to main's shape. The kit moves to 0.12.16 (saas-ui, and ui with it) and the SDK to 0.3.36, past what v0.0.105 released; saas-ui's SDK floor rises to ^0.3.36 because the form sends fields only that SDK carries. package-lock.json is regenerated by npm. Refs codefly-dev#1211 Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * feat(accounts): object-storage sources read through the composed object-storage service; the host's store client is deleted The host composes service-object-storage and reads every object-storage datasource through its per-call bucket binding (codefly-dev/service-object-storage#108, pinned at 5708d5e): the binding names the source's endpoint, region, bucket and prefix and carries its access key for that one call. objectstore.ServiceBucket pages the listing (bounded) and reads each object at If-Match its listed ETag; it asks Capabilities under the binding first and refuses a service that reports no bound caps, since a release from before the binding would serve its own bucket as the tenant's. runUploadSync takes its snapshot through objectstore.FilesConnector and reads it in batches; the source's key filter and object cap ride on SourceConfig.Scope and are applied before the version is digested. The delivery and the closing listing are unchanged. client.go, its tests and its egress guard are deleted. The provider stays registered non-conformant, now for clause 8 alone: the service's bound read takes one key per call. Refs codefly-dev#1211 Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * build(ci): the object-storage service is covered by the Codefly vendor image audit Every topology service declares image coverage. The object-storage agent runs its gateway and MinIO images, both pinned by the agent, and builds nothing in this repository, so it is classified as Redis and Vault are. Refs codefly-dev#1211 Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * build(accounts): the object-storage dependency's generated client, from codefly sync service accounts Declaring accounts' dependency on object-storage/grpc makes the go-grpc agent's sync emit a client file for it under code/external, and the sync-drift phase compares the tree against that output. Generated with CI's pinned codefly; the same command on clean main changes nothing. Refs codefly-dev#1211 Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * feat(accounts): object storage reads a batch in one call and is registered conformant service-object-storage#111 at 33ba749 serves a batched bound read: GetMany takes up to max_get_many_items keys, each at its listed ETag, and streams them back in request order. ServiceBucket.Read now issues one GetMany per batch, refuses an answer it cannot attribute (out of order, short of the listed size), maps CHANGED and NOT_FOUND to ErrObjectChanged and TOO_LARGE to ErrItemTooLarge, and refuses a service that reports no batch size as it refuses one that ignores the binding. The conformance suite now runs through ServiceBucket over an in-process fake of the service's gRPC API, so clause 8 is measured on the calls the host makes: 120 objects cost one GetMany after the listing. The provider is registered through connector.Budgeted, metered per source credential like GitHub; GetDatasourceCatalog reports accepts_new_sources and AddSource takes a new source with its prefix and key filter. The registration gate finds a connector's suite in the package that implements it, through the meter. Refs codefly-dev#1211 Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * test(accounts): a vanished store never closes an object-storage sync, and a missing bucket is never an empty listing The service at 33ba749 can answer a store-level failure inside GetMany as NOT_FOUND for every object, ending the stream successfully. The host's tombstone authority is the closing listing alone, sent only after a successful listing in the same sync and the delivery of every object in it; a NOT_FOUND read is ErrObjectChanged and fails the sync first. These tests pin both halves: a sync whose every read is NOT_FOUND sends no listing, and a List the service answers NOT_FOUND is the store's error, never an empty version. Refs codefly-dev#1211 Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * build(accounts): service-object-storage at eef1863, codefly-dev#111's head; the client package is unchanged Refs codefly-dev#1211 Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * fix(accounts): an object-storage source lists its literal prefix, admission needs a service that can read, and one credential spends one budget From the review of codefly-dev#1235 at a3c4836: - A source's prefix is a literal S3 key prefix, and the service normalizes a binding's prefix as a folder: "/reports/" listed "reports/", and "/" listed the whole bucket, so a populated source could read an empty scope and close with a listing that tombstones every entry. The binding now carries no prefix; every page filters on the literal prefix, which the service passes to the store unchanged, and keys travel whole. A listed key outside the prefix, or a page past the service's own page size, refuses the listing. - Every call to the service has a deadline (30 s; five minutes for a batched read), so a store that answers and then stalls cannot hold a sync and its worker's lease. - Admission needs a composed service that can read a bucket. work.go asks the service, unbound, whether it reads a batch (objectstore.CheckService); with none, or the released 0.0.18 agent, object storage is unavailable: the catalog says so and AddSource refuses with ErrObjectStorageUnavailable. - An upload source's budget is keyed by its credential — a keyed digest of the store's origin and access key id, fixed at connect, as an API source's is — so sources sharing one key share one quota and one provider throttle. A sync a person asked for is interactive and may spend the reserved share. Refs codefly-dev#1211 Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * style(accounts): the object-storage connector's two unchecked writes, which golangci-lint flagged on this branch A write into a sha256 hash cannot fail, and the test fake's page-token parse falls back to the first page; both discard the error explicitly. Refs codefly-dev#1211 Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * build(accounts): service-object-storage at a3157779, codefly-dev#111's fixed commit for the codefly-dev#1235 review The service now fails a bound listing the store marks truncated without a continuation token, keeps a binding's prefix literal, bounds every store response to 5 MiB and deadlines each bound operation at 30 seconds. The proto changes in comments only, so the client package is unchanged. Refs codefly-dev#1211 Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * test(accounts): the pinned object-storage service is held to the two invariants the host's deletions rest on; pin 358091a0 The closing listing of an object-storage sync authorizes deletions, and two properties of it were the service's alone: that a bound listing's prefix is used literally, and that a listing the store marks incomplete fails instead of answering as complete. Both were wrong in the service once, and a fake of the service cannot catch a regression in it. These tests build cmd/service-object-storage from the module go.mod pins, run it on an in-memory bucket of its own with a scripted S3 endpoint admitted as a composed dependency, and read through ServiceBucket: the store must be asked for "/reports/", "/" and "a//b/" exactly, and an IsTruncated page with no continuation token must fail the listing, empty or not. They fail rather than skip when the gateway cannot be built. Against the pre-fix service (33ba749) the incomplete-listing test fails; against a service patched to strip a leading slash, the prefix test fails. The service pin moves to 358091a0, codefly-dev#111's head after its second review round. Refs codefly-dev#1211 Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * style(accounts): the contract test's two unchecked closes, which golangci-lint flagged Refs codefly-dev#1211 Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * test(accounts): a composed object-storage service that cannot serve a binding is refused at admission, not at every sync Review r1's major 3 was that the catalog accepted object-storage sources, and AddSource stored them, while the composed runtime (the released 0.0.18, which knows no binding) failed every sync. The refusal now lives in the one place object storage is wired: Service.SetObjectStorage asks the service whether it serves a batched, bound read and refuses to wire one that does not, so the catalog does not offer the provider and AddSource refuses it by name. The test drives that wiring with a service answering as 0.0.18 does and asserts the refusal at admission — catalog, AddSource, nothing stored or audited — and that a service with the binding is admitted. Wiring whatever is composed turns it red. Refs codefly-dev#1211 Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * build(accounts): object storage runs on service-object-storage v0.0.19, in both pins, and acts on the reason each refusal names The composition selected agent 0.0.18, which has no binding, so on the declared runtime the host refused object storage at admission. v0.0.19 is the release of codefly-dev#111 (per-call binding, batched read, literal prefix, complete or failing listings, bounded responses, per-operation deadlines); the agent version moves to it with `codefly update service`, which rewrote only the version token, and accounts' go.mod requires the same release. v0.0.19 also names every refusal's reason in a google.rpc.ErrorInfo, because one code covers refusals that call for opposite actions. The host read RESOURCE_EXHAUSTED as a throttle — so a listing too long, or a response too large, blocked the credential for every source sharing it and retried the same listing forever — and PERMISSION_DENIED as a refused credential even when the guard refused the endpoint. storeError now acts on the reason: a too-long listing or response is ErrListingIncomplete, an object too large is ErrItemTooLarge, the guard's refusal is ErrDestinationRefused, and only STORE_THROTTLED (or a throttle with no reason) is a rate limit, with the store's RetryInfo. A contract test holds the released gateway to it: a page of 1,001 entries must read as an incomplete listing, never a rate limit; with the code-only mapping it reads as one. Refs codefly-dev#1211 Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * fix(accounts): a sync never empties an object-storage source that has files, each refusal gets the action its reason names, and one credential is one budget for every source From review r2 of codefly-dev#1235 at 32eb6c8: - g2 (owner decision): a closing snapshot with no files tombstones every entry mirrored from a source, and a service that wrongly answers "empty" — a rewritten scope, a discarded truncation — was proved twice through the real client and the production sync path. The host owns deletion authority, so the rule lives here, whatever any service version answers: a listing that comes back empty for a source whose last closing snapshot named objects is refused (ErrUploadEmptyListingRefused, job code datasource.upload_empty_listing_refused), closes nothing, and leaves the collection as it is. "Has files" is the count the host records when it sends a closing snapshot (datasource_sources.last_snapshot_items, migration 47); a source synced before it is unknown and treated as having files. The retained manifests cannot serve: every listing writes one, so a run of refused syncs would push the last non-empty one out. - g3: typed reasons reached distinct Go errors but one generic retry. Now a throttle is retried no earlier than the store's reset; an object over the service's cap is skipped and the sync goes on, surfacing it without a closing snapshot; a guard refusal or a rejected access key parks the source as degraded with a fixed reason and fails the job terminally; and LISTING_INCOMPLETE is the incomplete-listing disposition. - g1: the budget key was written at connect and only with the host's link key, so existing and keyless sources kept a budget each. It is now derived from the source's endpoint origin and access key id on every use — plain configuration on the same row, no secret — so every source on one key shares one quota and one provider block, with no backfill. - g4: the adapter comment no longer says the service normalizes a binding's prefix. Each behaviour has the test that would have caught it, driven through the real client and the job handler where the review's probes were; each was mutated red. Refs codefly-dev#1211 Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * fix(accounts): a refused object-storage source parks through the one park step, and a park that cannot be written retries Round two's refusal parking re-implemented the GitHub public-source park's control-plane write, and dropped its rule: when the degrade write fails, the job retries instead of going terminal, so a source never stops with nothing on it to say why. TestControlPlaneCallSites refused the copy as a new call site. parkDatasourceSource now owns the write and the already-parked check; the GitHub path and the object-storage path both call it, so the call-site list changes by a rename only. A refused upload whose park fails returns the park failure in place of the refusal and retries; TestUploadRefusalWhoseParkFailsRetries holds it, mutated red. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * chore(frontend): kit 0.13.3 and saas-sdk 0.3.37, bumped from main's numbers Main moved @codefly-dev/ui and @codefly-dev/saas-ui to 0.13.2 and released saas-sdk 0.3.36 without this branch's key-pattern fields, so both carry this branch's changes under versions the registry already serves. The kit moves together to 0.13.3; saas-sdk to 0.3.37, which saas-ui's peer floor and the frontend's exact pin now name, since 0.3.37 is the first release carrying an upload source's include and exclude patterns. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * fix(store): renumber the object-storage migrations above main's frontier 48 codefly-dev#1254 landed migration 48, so this branch's unshipped 46 and 47 now sit below the reference frontier and the migration reference gate refuses them. They become 49 (datasource_object_manifests) and 50 (last_snapshot_items), content unchanged, with the prose that names them. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * fix(store): renumber the object-storage migrations above main's frontier 54 codefly-dev#1217 landed migrations 49 to 54, so this branch's unshipped 49 and 50 collide with main's authority_delivery and installation_exposure_revision. They become 55 (datasource_object_manifests) and 56 (last_snapshot_items), content unchanged, with the prose that names them. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * fix(store): renumber the object-storage migrations above main's frontier 55 Main's hop admission keys took 55, colliding with this branch's 55. The two unshipped migrations become 56 (datasource_object_manifests) and 57 (last_snapshot_items), content unchanged, with the prose that names them. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * fix(store): renumber the object-storage migrations above main's frontier 58 Main's authority core took 56 to 58, colliding with this branch's 56 and 57. The two unshipped migrations become 59 (datasource_object_manifests) and 60 (last_snapshot_items), content unchanged, with the prose that names them. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Antoine Toussaint <312536060+antoinetoussaint-byte@users.noreply.github.com> Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Refs #1211, section (d): key patterns on a bucket source. Host-side only; codefly-dev/service-object-storage#108 needs nothing for it. Based on
mainand independent of #1213 and #1214 (see "Merge order").What changes
UploadDatasourceConfiggainsinclude_patternsandexclude_patterns. Each is at most 16 RE2 expressions of at most 256 characters, matched against the object key with the prefix removed: unanchored and case-sensitive, with^/$and(?i)available. Exclude wins, and an empty include admits everything. It also gainsfile_extensionswith GitHub's semantics, intersected with the patterns.UpdateSourceRequesttakes the patterns as fields 7–8 and its existingfile_extensions(5), andSourceFetchScopecarries the patterns too, so theexpectedrule covers them.AddSourceandUpdateSourcecompile every pattern. An invalid one, an empty one, more than 16, or one too long is refused asINVALID_ARGUMENTwith agoogle.rpc.BadRequestfield violation and a message that leads with the field path:upload.include_patterns[1]: "(" is not a valid RE2 expression: …on add, andinclude_patterns[1]: …on update. That leading path is stated in the proto comment as the contract, because the TypeScript SDK carries no error-detail types.objectstore/client.go, the file Object-storage datasource: the contract says a client offers and flags providers by the catalog, no client reads it, and the connector that exists cannot take a new source (no cursor, no push, no admission) #1211 deletes; it is in the connector package beside it (the owner's review of 2026-10-23).objectstore.Scope(scope.go) holds the host'sInScopepredicate (uploadKeyScope), as the GitHub connector'sSourceConfig.InScopedoes, together with the object cap.Windowapplies both to whatever listed the prefix. It drops a refused key before counting it, so an excluded object is never fetched and never uses upmax_objects.client.gonow returns the store's page whole, and the cap it used to apply moved intoScopewith the filter. When the object-storage service replaces the client, the filter and the cap stay where they are. The closing listing is built from what was delivered, so it names only matches.UpdateSourceon an upload source replaces the patterns and file types and refuses branch and paths. It runs under the same lock,expectedcheck andsource.updatedaudit event as a GitHub edit (bucket,prefix,include_patternsandexclude_patternsare declared on the event). It then queues the source's pull sync. That sync's closing listing omits what the narrower filter excludes, and the store tombstones it.v0.0.105released saas-ui 0.12.15 and saas-sdk 0.3.35, so this PR moves@codefly-dev/saas-uiand@codefly-dev/ui(co-versioned) to 0.12.16 and@codefly-dev/saas-sdkto 0.3.36. These are patches, because the changes are additive. saas-ui's SDK floor rises to^0.3.36, because the form sends fields that only that SDK carries, and the README floor sentence says why. The portal's exact SDK pin follows, as in feat(saas-ui): a person connects a generic OAuth api source through the kit #1201.package-lock.jsonwas regenerated bynpm install --package-lock-only, not edited by hand. feat(saas-ui): the datasources panel offers what the connector catalog admits and names the rest #1214 makes the same ui/saas-ui bump, so whichever lands second has nothing to add.ConnectObjectStorageFormtakes include/exclude patterns, one per line (a comma is part of a pattern), and file types. It places a host refusal under the field it names.objectStorageInputFrommaps its values for the client's new optionaladdObjectStorageSource. The form and its sharedCollectionFieldmove here from feat(saas-ui): the datasources panel offers what the connector catalog admits and names the rest #1214, which keeps only the catalog read, because the form's purpose is now the key filter. Nothing mounts the form yet: the host refuses new object-storage sources until the provider is conformant, and mounting it is part of the change that admits it.The five tests the issue requires
TestUploadKeyScopecovers precedence, matching relative to the prefix, case and file-type intersection.TestNormalizeKeyPatternsRefusesByNamecovers an invalid RE2 (including a backreference) refused with the pattern named, the count and length caps, an empty pattern, and the field in both message andBadRequest.TestAddSource_UploadRefusesAnInvalidKeyFilterByField(include, exclude and file type, each by field, nothing stored) andTestUpdateSource_UploadRefusesAnInvalidKeyFilterByField(an invalid exclude and paths on a bucket source, each by field, nothing written, no audit). The add test goes throughAddExistingSource, the test-only path into the sameaddSourcevalidation. TheAddSourceRPC itself admits before it validates, by design, and refuses the provider while it is non-conformant.datasource_upload_mirror_test.goseam: only matching keys are enqueued, the fetch log shows an excluded key never read, and the closing listing names exactly the matches. The source's cap is 3 against four listed keys, three of them matches, so an excluded key that counted against the cap would leave the listing incomplete.TestWindowpins the same rule at the connector: the cap counts only admitted keys, and a cut is reported.UpdateSourcewith a tighter exclude queues a re-sync, and its closing listing drops exactly the newly excluded key and nothing else. A later edit against the old scope is refused asErrDatasourceSourceScopeChanged.connect-object-storage-form.test.tsxcovers patterns and file types submitted intact (a pattern containing{1,3}survives), a host refusal shown under the field it names and not repeated, and an unplaced refusal kept in the form's slot.Each guard was checked by mutation, and each mutation turned a test red: no filter passed at sync (3/4), exclude no longer winning (1/3),
Windowcounting before matching (TestWindowand test 3), noexpectedcheck on an upload edit (4), and the kit's refusal parser not recognising fields (5).Reuse
normalizeFileExtensions(the matcher sits beside them indatasource_file_filter.go), the GitHub connector'sSourceConfig.InScopeshape (the host builds the predicate, the connector applies it), theBadRequestrefusal precedent insource_operation_invoke.go,UpdateSource's lock/expected/audit structure and its store write (the config JSONB already holds the upload config, so there is no migration),enqueuePullSyncfor the re-sync (it now returns the job id), and the GitHub form's collection field.datasource.github.reconcile), which is the wrong engine for a pull source; and generated TypeScript error-detail types, a larger SDK change than this needs while the message carries the field.Fix or hack
A fix at the owner: the filter is per-source host config, applied by the connector to the listing. Nothing in it is transitional.
objectstore/client.go, the engine #1211 deletes, holds neither the filter nor the cap; it only lists.runUploadSyncis the one call site that changes when the envelope connector replaces it, andScopegoes with it unchanged.Generated code
Proto, Go, the frontend
_pb.ts, the catalogs and the exported contracts come from CI's pinned codefly (source5234ce24). The TypeScript SDK comes from codefly 0.1.191, the release that generated main's copy. For each tool, the no-change control on cleanmaincame back clean first. Regenerating the SDK with CI's pin instead rewriteslibrary.codefly.yaml'sgenerated-bystamp on unchanged input, so I did not use it for the SDK.generate contracts --checkand the module-packagerun-generatorsdrift gate pass, and only the contract digests moved inmodule.package.codefly.yaml.Verified
go test -p 1 ./..., DB packages included), rerun after the move intoScope: 50 packages passed in the run, andpkg/auth/pg, whose harness failed to reach the codefly socket before any test ran, passed when rerun alone. TheScopemutations (count before filter; noInScopeat sync) each turnTestWindowand test 3 red.tsc, ESLint and its 350 tests. Portal:tsc, the type-primitive and inventory gates, and the fullpureproject (4,893 tests in 345 files).kit-version.mjs checkpasses on this branch merged into currentmain, which contains thev0.0.105tag. My earlier "rides main's unreleased versions" was checked against a base that predated that tag, and CI's red on "Frontend kit version" / "Release gates" was that.Not verified
codefly run. Admission refuses new object-storage sources and the dev fixture seeds none, so no source exists to filter. The behaviour is proven on the sync seam and the service tests only.datasourceConfigJSON/scan of the struct, and I did not add a DB test for it.Size
About 875 net non-test lines against the merge base, counting docs, the 73-line proto change and the version bumps, and excluding generated files, the lockfile and
ui-inventory.json. About 330 of those are the object-storage form and collection field moved here from #1214, so this PR's own work is about 545. Tests add about 500. Moving the filter into the connector added about 30 lines net:scope.go(50) against the cap and match it took out ofclient.go. That is just over the ~500 budget. I kept the kit test with the PR that needs it rather than split it off.Merge order
This PR and #1214 add kit lines at the same anchors (a client call, a hook, the export lists, the README hooks line). Whichever lands second needs a trivial rebase. #1213 is independent of this PR, and the two touch disjoint files in
objectstore. Whichever of them lands second gives #1213'sFilesConnectorthisScopein itsSourceConfig, applied inChangesbefore the listing is digested. That is a few lines of wiring, and the filter itself does not move.Docs
module/services/accounts/AGENTS.mdgains "An object-storage source is a prefix and a key filter", which says the connector'sScopeapplies the filter, not the listing source. The kit README gains the form,objectStorageInputFromanduseAddObjectStorageSource. The proto comments state the semantics and the refusal shape.🤖 Generated with Claude Code