fix(platform): retire dead shared vocabulary and complete the config lanes - #3260
Merged
Conversation
lib/shared carried modules whose only importer was their own test: the 0.4 Convex/Puck platform vocabulary under lib/shared/platform (render kinds, function bindings, run-capacity indicators, connector list pages — everything but when_predicate.ts, which the task board still uses), the provider-locale / model-variant / model-list / file-url-batch helpers written for 0.4 screens 0.5 never ported (model-list was also a second copy of @tale/shared/utils/model-list), the agent/workflow canonicalizers in canonicalize-config.ts (only sortObjectKeysDeep is read, by file_io.ts), and formatZodErrorFull whose "CLI gate" consumer never existed. Delete them. Drop the knip parking lines that hid the platform tree and the two live modules metrics-window.ts / text-matching (they have real importers) so knip audits them again — that surfaced one dead export in each (utcDayStart is internal; BoundaryMode was never used), fixed here. Findings: lib-shared-rest-1, lib-shared-rest-3, lib-shared-schemas-11.
lib/shared/constants/system-message-tags.ts declared a `[TAG]` prefix contract for system-role chat rows that no 0.5 module writes: the six body format/parse helpers had no caller at all, and the only reader of SYSTEM_MSG_TAG / parseSystemMessageTag / getSystemMessageDisplay was system-notice.tsx, whose own header admitted "nothing on this branch writes those tags yet". The needs the tags once served were rebuilt on another channel (StepLimitNotice reads usage.stepLimitHit; the arena copies an untagged system row), and the mock's `[human_input_response]` clause simulated a request_human_input resume the 0.5 chat has no tool for. Delete the module, its test, SystemNotice (+ test) — a system row now renders through MessageParts like every untagged row already did — the mock clause with the contract test that only existed to exercise it, and the knip parking line. Finding: lib-shared-rest-4.
lib/shared/config/registry.ts still narrated the 0.4 Convex split: a `readContext` (node-direct / v8-action / v8-sync), a `dataModel`, a `watcher` spec nothing ever populated, `V8_SYNC_DOMAINS`, `getV8SyncSpec` and a `configCache` mirror table no migration creates. The one live reader (organizations/scaffold.ts) only uses `name`, `scaffoldKind` and the per-key schema lookup — a "which seeded file gets which schema" table the `v8Sync` name misrepresented. Drop the retired fields, types and helpers, rename `v8Sync` to `seedSchemas`, rewrite the header and entry comments onto the file-tree contract, un-export what nothing imports, and remove the knip parking line so the module is audited again. scaffold.ts follows the rename and loses the sentence that pointed at a deleted `getConfigDomain` caller. Finding: lib-shared-rest-5.
server.ts and the Vite inject-env plugin both computed `MICROSOFT_AUTH_ENABLED: !!process.env.AUTH_MICROSOFT_ENTRA_ID_ID` into window.__ENV__, lib/env.ts typed it with its own getEnv overload and default branch, and vite-env.d.ts declared a VITE_ twin — yet nothing reads any of them: the Microsoft sign-in button is gated by the org's SSO config (`ssoConfig?.enabled`), not by this flag. An operator reading the servers would wrongly conclude AUTH_MICROSOFT_ENTRA_ID_ID toggles the client. Remove the field from every injection site, the env accessor and the two test fixtures. backend/auth keeps reading AUTH_MICROSOFT_ENTRA_ID_ID. Finding: lib-shared-rest-6.
lib/config-watcher.ts had been a permanent no-op since the backend
rewrite: createConfigWatcher logged "stubbed" and never invoked a
callback. Everything downstream was still live and switched on —
TALE_FILE_EVENTS=true in every dev compose, server.ts mounting the
authenticated per-org SSE door and raising Bun's idleTimeout for it, the
Vite plugin serving the same door, and use-file-events.ts holding a
reconnecting EventSource in every tab — so each tab paid for a stream
that could never carry an event, and the promised on-disk edit → cache
invalidation never fired.
Restore the producer with chokidar (already a dependency): watch
$TALE_CONFIG_DIR recursively, map `<orgSlug>/<domain>/<item>…` onto the
event the frontend cache keys on ({ type: <domain dir>, orgSlug, slug }),
skip dot entries (.history/, atomic-write temp files) at the watch level,
coalesce the burst one write produces into one event, and tear the
watcher down on close(). The domain is the dir name rather than a
registry lookup because live surfaces such as branding/ are not
registered. Every event carries an orgSlug, which the server's
default-deny fan-out requires. The Vite plugin drops its dead `examples`
fallback and says so when TALE_CONFIG_DIR is absent instead of watching
nothing silently.
The env reference documented this switch under the wrong name
(FILE_EVENTS_ENABLED is the window.__ENV__ field) with a wrong
description ("OneDrive-sync connector"); the row now names
TALE_FILE_EVENTS and what it does, in en/de/fr.
Finding: lib-shared-rest-2.
safeFetch and safeFetchBinary each carried their own copy of the redirect loop, and both replayed the original method, body and content headers against every Location: a POST answered with 303 — the common "POST /things → 303 /things/123" pattern — re-executed the POST with its JSON body (Authorization still attached on same-host hops), performing the mutation twice or handing a body to a read endpoint. RFC 9110 §15.4.4 requires GET after a 303; 301/302 to a POST switch to GET in every mainstream client. Real non-GET callers exist (tts/service.ts, connectors/live-host.ts). No test covered redirects at all. Fold the two loops into one fetchFollowingRedirects; per hop, a 303 (any method but HEAD) or a 301/302 to a POST continues as GET with no body and without Content-Type/Length/Encoding, while 307/308 keep method and body. Cross-host credential stripping and per-hop re-validation are unchanged. Tests pin POST→303, POST→301/302, POST→307/308, GET→302, HEAD→303 and the binary sibling with a scripted fetch. Finding: lib-shared-rest-7.
chatFilterConfigSchema.preferNonStreamingForFiltering was a live control in Settings > Governance > Content safety — toggled, saved into the org's chat-filter policy, seeded in the builtin catalog and the docs-demo fixture — and no backend module ever read it: the guardrail chain (lib/chat/guardrails.ts) streams the same way whatever the value. An admin control that promises to change streaming and changes nothing is a misleading control. Remove the field from the schema, the toggle row with its state and save wiring from the form, the contentSafety.preferNonStreaming* strings from en/de/fr, and the key from the catalog file and the e2e fixture. The schema strips unknown keys, so an org file that still carries the key keeps parsing. Finding: lib-shared-schemas-7.
harnessDefinitionSchema.pinnedVersion was declared in all nine harness.yml files, yet no loader, test, image build or runtime read it: the versions actually installed are the independent ARGs in services/sandbox-runtime/Dockerfile, and nothing cross-checked the two. qwen-code shows how such a copy rots — its yml pinned 0.10.3 while the image installs no qwen CLI at all. A pin nothing validates misleads an operator into believing a mismatch would be caught. Remove the field from the schema, the nine ymls (with qwen's "MUST be re-verified" note) and the test fixture; the Dockerfile ARGs stay the single source of the installed versions. Finding: lib-shared-schemas-9.
enterprise_sso.ts kept a private emptySsoConnectionFile() nothing in the file (or anywhere else) called, and ssoUserInfoSchema declared companyName and customAttributes that no OIDC/OAuth2/Graph adapter ever populates and no provisioning code reads — identity fields the boundary promised and no protocol front-half produced. Delete the helper and the two fields; the adapters' SsoUserInfo values are unchanged. Finding: lib-shared-schemas-10.
larryro
force-pushed
the
fix/shared-lib-dead-vocabulary
branch
from
September 6, 2026 02:16
9ecc10d to
31d75ed
Compare
automationPresentationSchema.icon and .labels were copied from every pack manifest into automations.presentation on upload and on provisioning, and returned by the list route, but no surface read them: the automations list showed name + slug only, so the glyph and the catalog chips all nine shipped packs declare were stored and never seen. Both the manifest and the presentation schema are strict, so dropping the two fields would have refused every existing pack carrying them and needed a migration for the stored rows; the doc-comments name the surface they were meant for. Complete the path instead: the presentation module gains automationDisplayIcon (the Iconify id ConfigIcon resolves offline) and automationDisplayLabels beside automationDisplayName, and the list's name cell renders the glyph like the skills table does — neutral fallback for canvas-authored automations — with the labels as slate chips beside the name. Docs (en/de/fr) name the two additions. Finding: lib-shared-schemas-12.
larryro
marked this pull request as ready for review
September 6, 2026 02:31
…ocabulary # Conflicts: # docs/de/self-hosted/configuration/environment-reference.md # docs/en/self-hosted/configuration/environment-reference.md # docs/fr/self-hosted/configuration/environment-reference.md
…ocabulary # Conflicts: # knip.config.ts
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.
Summary
Shared-lib theme of the backend fix campaign: retire the 0.4 vocabulary that survived the rewrite with no reader, and finish the config lanes that were half-wired (the file-events watcher, redirect semantics in safeFetch, unread schema fields).
Findings fixed
/events/file(TALE_FILE_EVENTS) actually emits; Vite plugin guard; env-reference row corrected (TALE_FILE_EVENTS) in en/de/fr.[human_input_response]clause deleted.v8Sync→seedSchemas; V8 fields/helpers gone..strict()and all nine shipped packs declare icon+labels, so removal would refuse existing packs and need a migration. automationDisplayIcon/automationDisplayLabels resolvers added; the automations list renders the glyph (ConfigIcon, Sparkles fallback like the skills table) and the labels as chips. Docs catalog.md en/de/fr updated.Skipped
(none)
Tests & gates observed
bunx tsc --noEmit— exit 0bunx oxlint --type-aware— exit 0bunx vitest --run --project server—Test Files 531 passed (531)/Tests 6124 passed (6124)bunx vitest --run --config vitest.ui.config.ts app/features/automations/components/automations-list.test.tsx—Test Files 1 passed (1)/Tests 8 passed (8)git merge-tree --write-tree origin/main HEAD(main = 5127ecd) — clean, no rebase neededbun run check— exit 1 solely atformat:check:services/platform/backend/domains/tasks/reattach.stalled.test.ts— that file is byte-identical to origin/main (pre-existing, not touched by this branch). The pipeline short-circuits there, so its remaining stages were run explicitly:uvx ruff@0.15.0 format --check— exit 0bunx turbo run format:check—Tasks: 8 successful, 8 totalbunx turbo run lint typecheck test test:ui—Tasks: 40 successful, 40 total;@tale/platform:test: Test Files 539 passed (539) / Tests 73363 passed (73363);@tale/platform:test:ui: Test Files 454 passed (454) / Tests 3507 passed (3507);@tale/ui:test: 124/1181,@tale/web:test: 23/201,@tale/docs:test: 31/201all passedbun run knip:check— exit 0 (one configuration hint,cron-parserin ignoreDependencies, pre-existing on main — this branch's knip diff never touches that line)run-itest.sh, real Postgres):[itest] 495/496 checks passed across 135/135 lanes, noRUN TRUNCATED; the single FAIL iswebdav re-home, red on main before this campaign (webdav theme)Notes for the reviewer
{ type: <domain dir>, orgSlug, slug }— worth a look at the dev-compose path where TALE_FILE_EVENTS is on.Merged origin/main (42f903e) — 4080dff
Merge commit, no rebase. The only manual resolution was the "Feature flags" table in
docs/{en,de,fr}/self-hosted/configuration/environment-reference.md: main's rows are kept verbatim (#3248's fourTRUSTED_*_HEADERrows, #3262'sTALE_DEPLOYMENT_CONFIG_ADMINSwording) and this branch's correctedTALE_FILE_EVENTSrow replaces the staleFILE_EVENTS_ENABLEDone. The en/fr descriptions were trimmed by a few words so the row fits main's existing column width —git diff origin/main HEAD -- 'docs/*/self-hosted/configuration/environment-reference.md'is exactly one row per locale (3 files changed, 3 insertions(+), 3 deletions(-)).knip.config.ts,governance.tsand the threemessages/*.ymlauto-merged.Gates re-run on the merged head:
bun run --filter @tale/docs test(31 files / 201 tests),build:search-index(tree clean afterwards), services/platformbunx tsc --noEmit,bunx oxlint --type-aware,bunx vitest --run --project server(Test Files 550 passed | 1 skipped/Tests 6246 passed | 5 skipped), rootbun run knip:check(exit 0, same pre-existingcron-parserhint) — all green.Merged origin/main (c70872f) — 7d64ffd
Second maintenance merge:
knip.config.tsconflicted because #3253/#3256 dropped two ignore entries (lib/shared/constants/agents.ts,lib/shared/schemas/approvals.ts) from the same list this branch trims; resolved by keeping both sides' removals. Gates on the merge: platformtsc --noEmitclean,oxlint --type-awareclean, rootknip:checkclean (pre-existing cron-parser hint only).