Connect each acceptance criterion to the right evidence
A release has several outcomes: a build can finish, an image can be published, deployment can install resources and verification can test behavior. Before approving, identify which outcome demonstrates each requirement. In this lesson’s fictional case, a reporting application requires a valid functional contract and successful pre-production verification. The build tolerated contract failure and stayed green; subsequent verification also failed. The useful conclusion for the committee is that acceptance remains pending. Record the run, candidate release, unmet criterion, owner and next action. Do not substitute an aggregate color for these facts. In Cloud Deploy, retrying failed verification starts another run and can put the rollout into IN_PROGRESS. That state still establishes neither approval nor completed rollback. The APS team needs to know which service is currently exposed and which evidence is missing before preparing production transition.
Read failure, dependency and volume configuration
Pipeline review should answer three separate questions: what blocks, what waits and what persists? A step using allowFailure can fail without making the overall result a failure. For a mandatory test, review configuration and run a controlled negative test demonstrating the gate. Next, draw dependencies using step IDs. In the question’s fragment, waitFor with the start marker lets the publisher start without waiting for the contract, despite appearing later in YAML. Finally, distinguish ordering from data sharing: waiting for a step does not copy its /tmp into another container. If a report must cross steps within one build, use /workspace or a configured shared volume. Confirm write and read paths. The example.invalid images in the exercise are fictional reading identifiers, not an executable pipeline. During cross-team review, ask for a concrete demonstration of each of these three properties.
Distinguish configuration, state and remote objects
A Terraform review should retain three perspectives: intent written in configuration, the state representation and objects existing at the provider. An authorized manual change can create differences among them. A refresh-only plan proposes reconciling state and outputs with external changes; it is not a request to restore the remote object to its old value and does not rewrite HCL files. The plan command alone does not apply the proposal. After deciding how to record the intervention, review intended configuration and a normal plan before proceeding. The same care applies to classic import: associating an existing database with state does not demonstrate that a manual resource block represents every relevant attribute. Ask the owner to review differences and changes the provider intends to make. This exercise executes no Terraform and does not replace rehearsal in an authorized environment. Project decisions should distinguish completed import from safe, reproducible operational adoption.
Identify who authenticates each interaction
Success of a local command does not prove that an application uses the same identity. On a laptop, the gcloud session and ADC configuration are distinct. If a library cannot find credentials, first confirm the configured mechanism before requesting additional permissions. In an authorized development context, prepare ADC using the appropriate method and verify identity without exposing tokens in logs. With Cloud Tasks delivery, also separate task creation from authentication of the request to its target. In the scenario, the target receives an ID token with an unexpected audience; granting the producer more queue permissions does not correct that claim. Align configuration with the service contract and observe a new delivery. This lesson’s lab uses no credentials and invokes no services. When preparing handover, document identity, purpose, destination and evidence at each boundary, including behavior when authentication fails. Operations can then investigate the correct stage without broadening access by trial and error.
Exercise: finish a query only when continuation ends
The fictional inventory begins with an empty page and a continuation token. Its second page contains report-A and another token; the third contains report-B and ends the sequence. Before running, write the three expected requests and final list. Run python3 content/labs/pca-pagination-contract/run.py and compare your prediction with the result. The collector uses a local callback, retains parent, filter and page_size, and passes tokens without interpreting their contents. It continues even when items is empty. Positive page-count and page_size bounds are defensive choices of this teaching client, not the complete rules of an AIP-158 API, which also permits page_size zero using the service default. The exercise retains repeated values rather than silently removing them. Traversing pages does not guarantee a snapshot of a changing collection. For a decommissioning decision, the team still needs to define consistency, time scope and confirmation of actual dependencies.
Make partial failures visible to the consumer
An inventory script can collect some resources and fail on the next page. Returning that partial list as success makes a report confuse absence with collection failure. In the exercise, callback errors, invalid responses, repeated tokens and exhausted page bounds cause explicit failure; they do not return approved partial results. Inspect the implementation and change the sequence to repeat an earlier token. Explain why ending that cycle with an empty list would be dangerous for a decommissioning committee. The model copies requests and results so that mutation of a callback dictionary cannot change the next request’s scope. It implements no retries, quota management, credential expiration or distributed consistency. In a real system, define suitable time limits, error handling and observability. One product alternative is to present incomplete collection explicitly while preventing it from being interpreted as confirmation of an empty inventory. Choose that behavior deliberately rather than relying on an incidental exception handler.
Limit updates to intent and test rejection
When an API documents FieldMask, payload and mask have different roles. In the fictional resource, selecting enabled and sending false disables that field; also sending label=B outside the mask does not change label=A. Form the request from intent, not every field a form happens to know. A wildcard mask with full-replacement semantics can affect mutable fields unknown to an old client. The test should confirm both the intended change and preservation of other values. Apply the same reasoning to access controls: executing VerifyAPIKey does not prove rejection of an invalid key when continueOnError permits continuation and no other denial exists. For each mandatory control, prepare a negative test with an expected result and a requirement link. Examples use explicitly defined contracts for this exercise; do not assume every API or proxy has these configurations. Consult documentation and effective configuration before generalizing the conclusion to another service.
Close delivery with a decision RUN can support
In the final simulation, present a short record to the sponsor: failed contract, configuration tolerating that failure, failed pre-production verification and conditions for reassessment. Propose retaining the current service and replanning the window while owners investigate. If the business requests reduced scope, describe the exact changed criterion, required authorization and limitations of the new decision; do not retain a claim of full compliance. Then ask an operations colleague to find the candidate run, explain step dependencies, locate the report and distinguish an in-progress retry from acceptance. This exercise tests handover usefulness. In summary, retain five boundaries: aggregate result versus requirement, sequence versus persistence, state versus configuration, producer versus target authentication, and partial response versus completed collection. Related topics are data reconciliation, observability and change governance. Learning means being able to justify a decision with evidence tied to its requirement without promising what the rehearsal did not measure.
"""Original local exercise. No network, credentials, SDK or snapshot guarantee."""
from copy import deepcopy
import json
def collect(fetch, parent, query_filter, page_size=20, max_pages=100):
"""Traverse a synthetic List contract; fail instead of returning partial success."""
if not isinstance(parent, str) or not parent.strip:
raise ValueError('parent must be nonempty text')
if not isinstance(query_filter, str):
raise ValueError('filter must be text')
for value, label in [(page_size, 'page_size'), (max_pages, 'max_pages')]:
if type(value) is not int or not 1 <= value <= 1000:
raise ValueError(label + ' must be an integer from 1 to 1000')
base = {'parent': parent, 'filter': query_filter, 'page_size': page_size}
token, seen, items = '', set, []
for _ in range(max_pages):
request = deepcopy(base)
request['page_token'] = token
response = fetch(request)
if not isinstance(response, dict) or not isinstance(response.get('items'), list):
raise ValueError('response must contain an items list')
if len(response['items']) > page_size:
raise ValueError('synthetic response exceeds requested page size')
if any(not isinstance(item, dict) for item in response['items']):
raise ValueError('synthetic items must be dictionaries')
next_token = response.get('next_page_token', '')
if not isinstance(next_token, str):
raise ValueError('continuation token must be text')
items.extend(deepcopy(response['items']))
if not next_token:
return items
if next_token in seen:
raise ValueError('repeated continuation token')
seen.add(next_token)
token = next_token
raise ValueError('page bound reached before completion')
def scripted(pages):
"""Original in-memory fixture with exact opaque-token lookup."""
calls = []
def fetch(request):
calls.append(deepcopy(request))
return deepcopy(pages[request['page_token']])
return fetch, calls
def main:
names = []
def check(name, condition):
if not condition:
raise AssertionError(name)
names.append(name)
def rejects(name, callback, kind=ValueError):
try:
callback
except kind:
names.append(name)
else:
raise AssertionError(name)
pages = {
'': {'items': [], 'next_page_token': 'opaque_x7'},
'opaque_x7': {'items': [{'id': 'report-A'}], 'next_page_token': 'opaque_Z2'},
'opaque_Z2': {'items': [{'id': 'report-B'}]}
}
original = deepcopy(pages)
fetch, calls = scripted(pages)
result = collect(fetch, 'projects/fixture', 'state:READY')
check('empty intermediate page is traversed', result == [{'id': 'report-A'}, {'id': 'report-B'}])
check('opaque tokens are passed unchanged', [c['page_token'] for c in calls] == ['', 'opaque_x7', 'opaque_Z2'])
check('parent remains stable', all(c['parent'] == 'projects/fixture' for c in calls))
check('filter remains stable', all(c['filter'] == 'state:READY' for c in calls))
check('page size remains stable by client choice', all(c['page_size'] == 20 for c in calls))
check('input fixtures are not mutated', pages == original)
check('missing continuation ends traversal', len(calls) == 3)
f, c = scripted({'': {'items': [], 'next_page_token': ''}})
check('empty final collection is accepted', collect(f, 'p', '') == [] and len(c) == 1)
f, _ = scripted({'': {'items': [{'id': 'same'}], 'next_page_token': 'a'}, 'a': {'items': [{'id': 'same'}]}})
check('repeated resource values are retained', collect(f, 'p', '') == [{'id': 'same'}, {'id': 'same'}])
f, _ = scripted({'': {'items': [], 'next_page_token': 'a'}, 'a': {'items': [], 'next_page_token': 'a'}})
rejects('self-repeating continuation fails', lambda: collect(f, 'p', ''))
f, _ = scripted({'': {'items': [], 'next_page_token': 'a'}, 'a': {'items': [], 'next_page_token': 'b'}, 'b': {'items': [], 'next_page_token': 'a'}})
rejects('multi-token cycle fails', lambda: collect(f, 'p', ''))
f, c = scripted(pages)
rejects('page budget does not return partial success', lambda: collect(f, 'p', '', max_pages=2))
check('page budget limits callback invocations', len(c) == 2)
f, _ = scripted(pages)
check('completion on exact page bound succeeds', len(collect(f, 'p', '', max_pages=3)) == 2)
for value, label in [(0, 'zero'), (-1, 'negative'), (True, 'boolean'), (2.5, 'fraction'), (1001, 'above limit')]:
rejects('reject ' + label + ' page bound', lambda v=value: collect(lambda _: {}, 'p', '', max_pages=v))
rejects('reject boolean page size', lambda: collect(lambda _: {}, 'p', '', page_size=True))
rejects('reject zero page size in this client', lambda: collect(lambda _: {}, 'p', '', page_size=0))
rejects('reject blank parent', lambda: collect(lambda _: {}, ' ', ''))
rejects('reject nontext filter', lambda: collect(lambda _: {}, 'p', None))
for response, label in [(None, 'nonobject response'), ({}, 'missing items'), ({'items': {}}, 'nonlist items'), ({'items': ['A']}, 'nonobject item'), ({'items': [], 'next_page_token': None}, 'nontext token')]:
rejects('reject ' + label, lambda r=response: collect(lambda _: r, 'p', ''))
rejects('reject oversized synthetic page', lambda: collect(lambda _: {'items': [{}, {}]}, 'p', '', page_size=1))
def fails_second(request):
if request['page_token']:
raise RuntimeError('fixture transport failure')
return {'items': [{'id': 'partial'}], 'next_page_token': 'later'}
rejects('callback failure does not return partial data', lambda: collect(fails_second, 'p', ''), RuntimeError)
mutation_calls = []
def mutates_request(request):
mutation_calls.append(deepcopy(request))
token = request['page_token']
request.update(parent='changed', filter='changed', page_size=1)
return {'items': [], **({'next_page_token': 'a'} if not token else {})}
collect(mutates_request, 'original', 'ready', page_size=20)
check('callback mutation cannot change next request scope', mutation_calls[1] == {'parent': 'original', 'filter': 'ready', 'page_size': 20, 'page_token': 'a'})
external = {'items': [{'nested': {'value': 1}}]}
output = collect(lambda _: external, 'p', '')
output[0]['nested']['value'] = 9
check('output nested objects are isolated', external['items'][0]['nested']['value'] == 1)
print(json.dumps({'passed': len(names), 'checks': names, 'result': result, 'requests': calls, 'network': False, 'vendorExecution': False, 'persistentWrites': False}))
if __name__ == '__main__':
main
A green build tolerated a failed mandatory test; subsequent verification also failed. The team needs new evidence before approving.
Common pitfalls
Confusing green status with acceptance, ordering with file sharing, refresh with reversal, CLI login with ADC and an empty page with query completion.
Related topics: Reconciliation and complete inventories · Change governance and transition to RUN · Runtime identities and observability
Tie each conclusion to supporting evidence and explicitly test failures that must prevent approval.
Reference: Build configuration file schema · Current linked standard guide; edition date unconfirmed (2026-09-30 inspection)