Skip to content

Implement planned topic: 0011-external-storage - #209

Merged
brianstrauch merged 7 commits into
mainfrom
draft/0011-external-storage
Aug 24, 2026
Merged

Implement planned topic: 0011-external-storage#209
brianstrauch merged 7 commits into
mainfrom
draft/0011-external-storage

Conversation

@temporal-plugin-updater

Copy link
Copy Markdown
Contributor

Validation Report — external-storage

Scope: the two new reference files introduced on draft/0011-external-storage:

  • references/go/external-storage.md
  • references/python/external-storage.md

plus the one-line pointers added to references/go/go.md and references/python/python.md.

Source of truth: ../documentation/docs/. Primary subtrees consulted:

  • docs/encyclopedia/data-conversion/external-storage.mdx
  • docs/encyclopedia/data-conversion/codec-server.mdx
  • docs/develop/go/best-practices/data-handling/external-storage.mdx
  • docs/develop/python/best-practices/data-handling/external-storage.mdx

Integration topic? No — External Storage is a built-in SDK feature, not a third-party integration. Check 5 (integration-layout audit) does not apply.


Go/no-go

Check Result
Check 1 — citation audit PASS (Go 38/38; Python 39/39 → 100% overall, threshold ≥ 98%)
Check 2 — reverse-grep audit PASS with note (Go 0 misses; Python 2 import-path misses — minor)
Check 3 — regression patterns PASS (0 hits, including topic-specific)
Check 4 — independent re-verification PASS (Go 10/10; Python 10/10 → 100%, threshold ≥ 95%)
Check 5 — integration layout N/A (not an integration topic)
Check 6 — tone / scope audit PASS (0 findings)

Overall verdict: GO.

The two Python import-path misses (Check 2) fall under the MINOR FIXES bucket per §5: ≤ 5 unexplained misses that look like missing-citation-comment cases. They do not block merge. Optional follow-up: tag the two from temporalio.contrib.aioboto3 / from temporalio.external_storage lines with <!-- undocumented: source = inferred from docs symbols + SDK module convention --> or replace them with the exact import lines used by the upstream samples-python repo.


Check 1 findings — citation audit

Zero unresolved citations.

  • Go file: 38 citations extracted, 38 verified. Each <!-- docs/...:N-M --> resolves to a file under ../documentation/docs/, the line range exists, and the preceding authored claim is substantively supported by the cited text.
  • Python file: 39 citations extracted, 39 verified.

Spot-checked highlights that survived independent re-reading:

  • 2 MB per-payload limit, fixed on Cloud, configurable on self-hosted — encyclopedia/external-storage.mdx:42-45.
  • Pipeline order Converter → Codec → External Storage — encyclopedia/external-storage.mdx:72-73,98-99.
  • Go: PayloadSizeThreshold: 1 externalizes all, 0 is interpreted as the default (256 KiB) — develop/go/.../external-storage.mdx:232-233.
  • Python: payload_size_threshold=0 externalizes all — develop/python/.../external-storage.mdx:196-197.
  • Go custom driver has four methods (Name, Type, Store, Retrieve); Python custom driver has three (name, store, retrieve). Confirmed against the respective SDK docs.
  • StorageDriverActivityInfo is for standalone (non-workflow-bound) Activities only — develop/go/.../external-storage.mdx:128-130, 204-208.
  • /download, /decode, /encode, ?preserveStorageRefs=true endpoints — encyclopedia/codec-server.mdx:105-114.
  • NewPayloadHTTPHandler vs NewPayloadCodecHTTPHandlerencyclopedia/codec-server.mdx:96-103.

Check 2 findings — reverse-grep audit

Go file: zero unexplained misses.

All 21 Temporal Go API symbols (s3driver.NewDriver, s3driver.StaticBucket, s3driver.Options, awssdkv2.NewClient, client.Dial, client.Options, converter.ExternalStorage, converter.StorageDriver, StorageDriverClaim, StorageDriverStoreContext, StorageDriverRetrieveContext, StorageDriverWorkflowInfo, StorageDriverActivityInfo, StorageDriverSelector, DriverSelector, PayloadSizeThreshold, Drivers, Name(), Type(), Store(...), Retrieve(...), NewPayloadHTTPHandler, PayloadHTTPHandlerOptions, NewPayloadCodecHTTPHandler), all import paths (go.temporal.io/sdk/contrib/aws/s3driver, .../s3driver/awssdkv2, go.temporal.io/sdk/converter), all four endpoint strings, and all threshold sentinels appear in the source-of-truth docs.

Python file: 2 unexplained misses.

# Token Authored line Status
1 temporalio.contrib.aioboto3 line 46 (from temporalio.contrib.aioboto3 import new_aioboto3_client) Module path does not appear anywhere under ../documentation/docs/. The symbol new_aioboto3_client is documented (develop/python/.../external-storage.mdx:50) but its import line is hidden inside an external SNIPSTART file (features/snippets/external_storage/s3_setup/s3_driver_create.py) that is not part of the docs tree. Author inferred the path from the SDK's temporalio.contrib.* convention. Plausible but not directly verifiable against docs.
2 temporalio.external_storage lines 48 and 131 (from temporalio.external_storage import ...) Same situation: the symbols (ExternalStorage, S3StorageDriver, StorageDriver, StorageDriverClaim, StorageDriverStoreContext, StorageDriverRetrieveContext, StorageDriverWorkflowInfo) are all documented, but their import path is not.

Discussed but not a finding:

  • payload_size_threshold=1 (Python anti-patterns, line 229) — only appears in the negative bullet warning against using Go's sentinel. The cross-language statement "where 0 is the default and 1 externalizes all" is anchored in the Go docs (develop/go/.../external-storage.mdx:232-233). Valid cross-reference.
  • "Public Preview" (both files, line 4) — docs use "Pre-Release"; author flagged this in the immediately following <!-- docs/...:24-30 documents Pre-Release status --> comment, so the mapping is intentional, not fabrication.

Verdict: 2 misses fall under MINOR FIXES (≤ 5, missing-citation-comment style). Recommended fix is a one-line tag like <!-- undocumented: inferred from temporalio.contrib.* convention; cross-check against samples-python --> on the import block, or substitution of the verified imports from the upstream SNIPSTART files.


Check 3 findings — regression patterns

Zero hits. None of the universal regression patterns (--profile, the three legacy TLS env-var names, tcld service-account, --output text/jsonl, saas-api.tmprl.cloud:7233) appear anywhere in either authored file.

Topic-specific regression sentinels were checked:

  • PayloadSizeThreshold: 0 in the Go file appears only inside anti-pattern bullets explicitly warning against that misuse (lines 93 and 248) — correct guidance, not a regression.
  • payload_size_threshold=1 in the Python file appears only inside the anti-pattern bullet explicitly warning against that misuse (line 229) — correct guidance, not a regression.

The skill correctly captures the Go/Python sentinel asymmetry (Go: 1 externalizes all, 0 is default; Python: 0 externalizes all) and calls out the cross-language footgun.


Check 4 findings — independent re-verification (sampling)

Go file — 10/10 match.

Sampled authored lines (with cited docs): L6 (encyclopedia:24-30), L14 (encyclopedia:43-45), L21 (encyclopedia:72-73,98-99), L30 (encyclopedia:90-92), L85 (develop/go:41-42), L92 (develop/go:232-233), L107 (develop/go:249-252), L134 (develop/go:185-195), L143 (develop/go:123-134,204-208), L235 (encyclopedia:131-143).

All ten authored claims were re-formed independently from the docs and matched. Notable nuances that survived re-reading: 2 MB Cloud-fixed vs self-hosted-configurable, four-method custom driver, StorageDriverActivityInfo standalone-only, multi-driver retrieval semantics, parallel within a single Workflow Task.

Python file — 10/10 match.

Sampled authored lines: L6 (encyclopedia:24-30), L15 (encyclopedia:47-49,55-56), L21 (encyclopedia:72-73,98-99), L30 (encyclopedia:90-92), L76 (develop/python:83-84), L113 (develop/python:148-156), L123 (develop/python:95-144), L200 (codec-server:82-89), L212 (codec-server:200), L229 (develop/python:196-197).

All ten matched. The three-vs-four method count difference between Python (name, store, retrieve) and Go (Name, Type, Store, Retrieve) is correctly preserved per SDK.


Check 6 findings — tone and scope

Zero findings.

  • Pattern 1 (workaround / escape-hatch disclosure): none. Both files were grepped for if you really|as a last resort|if you must|undocumented way|you can technically|workaround|hack — no hits in either external-storage file. Anti-pattern bullets follow the supported "Don't do X. Use Y instead." form throughout.
  • Pattern 2 (in-the-weeds rationale): none. Each consequence bullet (encryption-before-upload, UI shows reference token, concurrency) maps to observable agent-visible behavior.
  • Pattern 3 (multi-sentence bullets): the prose is tight. The few two-sentence bullets each add new actionable information.
  • Pattern 4 (Public Preview admonition): both files carry the standard > [!NOTE] admonition with the canonical phrasing at line 4. The terminological gap between docs ("Pre-Release") and skill ("Public Preview") is acknowledged inline via the immediately following <!-- docs/...:24-30 documents Pre-Release status --> comment.

Statistics

Metric Go file Python file Total
Citations 38 39 77
Citations verified (Check 1) 38 39 77 (100%)
Tokens grep-checked (Check 2) ~30 ~34 ~64
Unexplained grep-misses 0 2 2
Regression hits (Check 3) 0 0 0
Check 4 sample size 10 10 20
Check 4 matches 10 10 20 (100%)
Check 6 findings 0 0 0

Verdict

GO — merge as-is.

Optional follow-up (not blocking): add an <!-- undocumented: ... --> tag on the two Python import statements (from temporalio.contrib.aioboto3 import new_aioboto3_client and the from temporalio.external_storage import ... blocks at lines 48 and 131) so the import paths are explicitly marked as inferred from the SDK module convention rather than from the docs prose. Alternatively, copy the verified imports from the upstream samples-python files referenced by the SNIPSTART markers in develop/python/.../external-storage.mdx.

Comment thread references/go/external-storage.md Outdated
Comment thread references/go/external-storage.md Outdated
brianstrauch and others added 6 commits August 21, 2026 14:45
Python:
- Import ClientConfig from temporalio.envconfig, not temporalio.client.
  load_client_connect_config() is a staticmethod on the envconfig class;
  the temporalio.client.ClientConfig TypedDict has no such member, so the
  snippet raised AttributeError. Follow main's env-config convention
  (setdefault target_host) from #261.
- Register real Workflow/Activity placeholders. Worker() with empty
  workflows and activities raises "At least one activity, Nexus service,
  or workflow must be specified", and wrap the setup in async main().

Go:
- Cover the GCS driver (contrib/gcp/gcsdriver + gcssdk), which the SDK
  ships and the docs install alongside S3.
- Load client options with envconfig.MustLoadDefaultClientOptions() and
  note that Workers inherit External Storage from their Client.

Align coverage across all three languages, each of which was missing
something the others had:
- 50 MiB MaxPayloadSize/max_payload_size ceiling and the matching
  anti-pattern (Go, Python).
- Store/Retrieve are not retried within a Task attempt; the Task retries
  as a whole, so storage must be idempotent (Go, Python).
- Multi-region durability with CRR + an MRAP ARN (Go, Python).
- Distinct driver names when registering two drivers of the same kind
  (Go, Python).
- Codec Server guidance (TypeScript), including that neither the
  TypeScript nor Python SDK ships a storage-aware handler.
- Built-in driver behavior sections (concurrency, content-addressed
  keys, integrity checks, diagnostics) in Go and Python.
- ctx.Context on the Go driver contexts, mirroring TypeScript's
  abortSignal guidance; optional type() override in Python.

Also: standardize the TypeScript Public Preview admonition on the repo's
wording, drop the transplanted `payloadSizeThreshold: 1` anti-pattern
(TypeScript compares >=, so 1 behaves like 0), replace site-relative
plugins-guide links with absolute URLs, refresh the index pointers, and
revert an unrelated whitespace change in the Spring AI reference.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Address code-review findings on the new external storage docs:

- Go: add missing "context" and "log" imports to the S3 driver, GCS
  driver, and client/worker setup snippets, which presented complete
  import lists but failed to compile.
- Go: add go.temporal.io/sdk/contrib/envconfig to both go get lines; it
  is a separate module and is imported by the setup snippet.
- Go: give the local-disk worked example an import block, and introduce
  the commonpb alias at its first use in the selector example.
- Go and Python: validate claim data in Retrieve/retrieve so a
  hand-crafted reference payload cannot read files outside the store
  directory, matching the hardening already applied to Store/store.
- Python: the Worker inherits the Data Converter from its Client and
  takes no data_converter argument; the prose said to pass it to both.

Verified by compiling every Go snippet against sdk-go and exercising
both path guards.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The new external storage docs were only reachable from the language
index files, so the paths an agent actually takes when a user hits a
payload limit still sent it to hand-roll the claim-check pattern.

- core/error-reference.md: TMPRL1103 recovery now points at built-in
  External Storage before manual reference passing.
- core/gotchas.md: the payload-limit fix notes the SDK does this for you
  in Go, Python, and TypeScript.
- core/patterns.md: Large Data Handling leads with the SDK-native
  option and scopes the manual pattern to the cases that need it.

Also link the Go external storage sample from the Codec Server section,
matching what the Python reference already does.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@brianstrauch
brianstrauch merged commit 5de78ea into main Aug 24, 2026
3 checks passed
@brianstrauch
brianstrauch deleted the draft/0011-external-storage branch August 24, 2026 19:25
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.

2 participants