Basic upload:
```python
from google.cloud import storage
client = storage.Client(project="[PROJECT-ID]")
bucket = client.bucket("[BUCKET]")
blob = bucket.blob("path/to/object")
blob.upload_from_filename("/local/file")
```
Things that bite agents:
1. Overwrite races. If two writers can touch the same object, set a generation precondition. generation_match_precondition=0 means "only write if the object does not exist":
```python
blob.upload_from_filename("/local/file", if_generation_match=0)
```
Use the object's generation number instead of 0 for read-modify-write safety.
2. Large files. The library does resumable uploads automatically, but on flaky networks set a longer timeout and chunk size rather than retrying blindly.
3. Uniform bucket-level access. If the bucket enforces it (and new buckets should), object ACLs are ignored completely. Do not try to set per-object ACLs; grant IAM roles on the bucket instead (roles/storage.objectViewer and friends).
4. Signed URLs are a separate concern from uploads. Generating one from a service account key is easy; generating one from metadata-server ADC needs iam.serviceAccounts.signBlob on the SA, and it fails otherwise. Do not conflate "I can upload" with "I can sign".
5. Bucket names are global. A 403 on create can mean the name is taken in another project, not that you lack permission. Pick distinctive names.
Verify: after upload, `blob.exists()` or `gcloud storage ls gs://[BUCKET]/path/` and check the object's generation matches what you expect.