OpenCyber Academy
Rapid course · Chapter 2 · English
Course code: OCA-016Chapter 02: OCA-016-C002

Chapter 02 — Alert triage and evidence preservation

Open, free learning for anyone who wants to protect people and technology. No account is required to read this text or its worked answers.

Five daily lessons · week 2 of four. This chapter groups the original section identifiers 2.x. Lesson numbers identify a learner day; section identifiers remain stable so existing source references and laboratory instructions still work.

Module map

Module 2 overview: five daily lessons below provide the teaching, examples, exercises and worked-answer links.

flowchart TD
  N0["Learning outcomes"]
  N1["2.1 An alert is a question to investigate"]
  N2["2.2 Prioritize harm, urgency and confidence separate"]
  N3["2.2a Reassessing triage priority under changing evid"]
  N4["2.2b TRI2 offline laboratory — reassessment under in"]
  N5["2.2c Two-role practical activity — frozen priority r"]
  N6["2.3 Preserve what supports the next decision"]
  N7["2.5 Contained lab: preserve and check a synthetic ex"]
  N0 --> N1 --> N2 --> N3 --> N4 --> N5 --> N6 --> N7
  N7 --> EX["Worked example"]
  EX --> Q["Exercise EX018"]

Module logical drawing

flowchart LR
  N0["Learning outcomes"]
  N1["2.1 An alert is a question to investigate"]
  N2["2.2 Prioritize harm, urgency and confidence separate"]
  N3["2.2a Reassessing triage priority under changing evid"]
  N4["2.2b TRI2 offline laboratory — reassessment under in"]
  N5["2.2c Two-role practical activity — frozen priority r"]
  N6["2.3 Preserve what supports the next decision"]
  N7["2.5 Contained lab: preserve and check a synthetic ex"]
  N0 --> N1 --> N2 --> N3 --> N4 --> N5 --> N6 --> N7 --> Q["EX018"]
[Learning outcomes]
    |
    v
[2.1 An alert is a question to investigate]
    |
    v
[2.2 Prioritize harm, urgency and confidence separate]
    |
    v
[2.2a Reassessing triage priority under changing evid]
    |
    v
[2.2b TRI2 offline laboratory — reassessment under in]
    |
    v
[2.2c Two-role practical activity — frozen priority r]
    |
    v
[2.3 Preserve what supports the next decision]
    |
    v
[2.5 Contained lab: preserve and check a synthetic ex]
    |
    v
[Worked example] --> [Exercise EX018]

Module learning outcomes

Across these five daily lessons, a learner should be able to:

  1. Turn a reported alert into a short triage question, without treating its label as a fact.
  2. Compare a small set of hypotheses against an evidence table and state what remains unknown.
  3. Set an initial priority using potential service harm, time sensitivity and confidence, then set a review time.
  4. Prepare a collection note that identifies the source, collector, authority, time and integrity check for a synthetic record.
  5. Explain why a matching hash helps detect change to a copy but cannot prove that the original source was truthful.
  6. Produce a handoff that another analyst can reproduce from the same supplied records.
  7. Normalize a supplied fixed-offset timestamp to UTC and distinguish an event's time from its discovery time and a query's coverage.
  8. Explain how a delayed log arrival can change a repeated query without changing the original event time, and audit the limits of a synthetic evidence package.
  9. Correlate two synthetic log sources with an explicit join key and time interval, marking ambiguous and unmatched rows without inferring a human actor.
  10. Prioritize bounded preservation requests by decision value, possible loss, authority and privacy, without reporting a requested copy as collected evidence.
  11. Distinguish a proposed preservation request, approved source hold, reported application, bounded effect check, expiry and release from a separately retained copy.
  12. Reassess a provisional priority at 09:05 and 09:20 using only released records, separating record availability, interpretation, cause, reported versus verified harm, source-preservation urgency and continuing-activity review.

Module 2 exercise rules

Use only records R1–R16 and the fictional authority described here; the exercise-only T1 card is available only where a question names it. No answer may claim to have queried a real service, received an unprovided identity detail, proved compromise or completed an export that the learner did not make. For each exercise, cite the relevant record IDs, state the observation before the inference, and give a bounded next action or test. The named output is the artifact to submit; the worked results at the end show acceptable reasoning.

Common procedure and expected evidence: (1) Write the exercise ID, case ID and evidence boundary at the top of the response. (2) Read the named input records and copy their IDs and relevant UTC times into the requested table or note. (3) Complete that exercise's output fields, marking unknown facts and unperformed actions rather than filling them with guesses. (4) Add the decision owner or custodian for any proposed collection or service change and a sentence limiting the conclusion. (5) Compare the resulting artifact with its matching worked result in the final appendix. Submit the named table or note with its source pointers and limits; a bare multiple-choice label or unsupported conclusion is not sufficient evidence of learning. No real account, patron record or production log is needed.

Score each numbered exercise 0 when its artifact is absent or asserts an unsupported fact, 1 when it addresses the question but omits a required evidence, scope or limitation field, and 2 when the stated fields are present, reproducible and appropriately authorized. A false claim about who controlled desk-a, a fabricated collection time or an invented log query must be corrected regardless of the other fields. This common rubric applies to all thirteen questions; a local instruction below adds required fields for a score of 2.

Lesson 06 — A bounded triage question and revisable priority

Week 2, day 1. Work through the teaching and the worked checkpoint before attempting the assigned exercise(s). Compare your artifact with the final worked result only afterward.

2.1 An alert is a question to investigate

Module 1 established owners and a bounded record. Triage now asks what the signal means, what might be affected, how soon a decision is needed and which authorized check would reduce uncertainty. A detector may produce the label “suspicious login,” but that label is neither an identity proof nor a conclusion that the account was compromised. The analyst must retain the detector's raw observation, collection time and rule context while testing alternative explanations.

For Harbor Library, the first two hypotheses are H1: the booking edits were scheduled training and H2: the account was used outside the planned training. A third possibility, H3: the patron's report reflects a display or synchronization problem, reminds us that the reported symptom and the booking changes might have different causes. These hypotheses are teaching devices. They are not exhaustive and should be revised when evidence appears.

Lesson table; scroll horizontally to read all columns →

Question Why it changes triage First authorized check
Are bookings still changing? Ongoing harm may require a faster decision. Request a current application audit summary from the service owner.
Was desk-a expected to edit these bookings? Authorized work can explain part of the timeline. Ask the training owner for the exercise plan and attendee confirmation.
How many patrons are affected? Scope matters for service and communication decisions. Compare the approved booking audit with the help-desk queue.
Can the logs be trusted and retained? A short retention window may make delay costly. Record the source, requested event range and proposed collection method; ask the custodian to confirm present availability, retention anchor and any actual deadline. Mark an unverified deadline unknown.

The initial action should be the smallest authorized check that could change the next decision. If a new edit is causing immediate harm, the incident lead can seek an urgent containment decision while collection continues. If the signal is stale and service impact is unknown, preserving records and checking the owner may be the more informative first step. Triage and containment can overlap; they are not a rigid sequence.

2.2 Prioritize harm, urgency and confidence separately

This book uses an illustrative classroom matrix, not a universal severity standard. Keep three judgments separate:

Lesson table; scroll horizontally to read all columns →

Dimension Low example High example What to write
Potential harm One corrected training booking Many lost bookings or sensitive-data exposure Who or what may be affected, with uncertainty
Time sensitivity Records retained for weeks; no ongoing changes Further edits occurring or evidence about to expire Next decision and its deadline
Confidence One uncorroborated alert Independent records agree on an unauthorized action Supporting records and contradictions

A case can be urgent even when confidence is low: continued edits could affect patrons while the cause remains uncertain. It can be well supported but low urgency after an authorized maintenance test has ended. The incident lead should record a provisional priority and the condition for changing it. Harbor Library's opening priority is “prompt review,” because a patron reported harm, yet the available records do not establish compromise or broad impact. This priority is a teaching judgment based on the fictional facts, not a prescribed incident category.

Two questions, four possibilities: authorized work can still leave a service problem

The priority matrix is easier to use when a question about permission is not allowed to answer a different question about service correctness. A planned operation can be performed incorrectly. An operation outside the applicable authorization can leave a value that happens to match the accepted target. Neither a good outcome nor a bad outcome identifies the human operator.

Use two propositions to make that distinction visible:

A and M concern different observations. The operation time and comparison time need not be equal. Connecting the later state to the earlier operation requires a separate causal argument; this model does not supply it. A result about one booking does not measure effects on all patrons.

Lesson table; scroll horizontally to read all columns →

Assignment What the two propositions say What they do not establish
A1M0 The operation was authorized; the specified comparison found a match. That every operation was authorized, that the whole service is correct, or who acted.
A1M1 The operation was authorized; the specified comparison found a mismatch. That this operation caused the mismatch, or that the operator was malicious.
A0M0 The operation was outside the applicable authorization; the specified comparison found a match. That the authorization failure is harmless, or that a matching result grants permission retroactively.
A0M1 The operation was outside the applicable authorization; the specified comparison found a mismatch. A causal connection between those findings, an attacker identity, or the population-wide impact.

These four assignments are logical possibilities in a deliberately small teaching model, not four causes, four incident severities, equally likely outcomes or calibrated confidence scores. Allowing all four combinations does not assert statistical independence. An unresolved proposition is not false: without an adequate authorization check, A remains unknown rather than becoming A0.

flowchart TD
  Q["Freeze object, operation, times and target version"] --> A["Authorization question: was this operation permitted?"]
  Q --> M["Service question: does observed state match the accepted target?"]
  A --> L["Record each bounded finding and its supporting source"]
  M --> L
  L --> C["Keep cause, human identity and wider impact unresolved unless separately supported"]
  C --> R["Propose the next authorized check; do not invent its result"]

Text alternative: Freeze the scope first. Ask the authorization and service-state questions through their respective owners. Combine only the findings actually supported for that scope. A combined finding still does not identify a cause or a person. Choose the next authorized check from what remains unresolved, rather than treating the diagram as permission to collect data or change a service.

Worked synthetic example, separate from R1–R16. Suppose a training owner supplies a verified, applicable instruction permitting operation E1 on invented booking X21 at 10:00. A service owner separately verifies that X21's value at 10:10 differs from its accepted target version V4. These premises select A1M1. They do not show whether E1, a later edit, an incorrect implementation or another mechanism caused the mismatch. The decision can require attention to service correctness even though the authorization question about E1 has been answered. No X21 observation becomes evidence in Harbor Library's canonical packet.

Reproducible worked example: eliminating assignments is not proving a cause

Run the following complete Python 3 program in a fresh session. It uses only in-memory synthetic propositions: no network access, credentials, files or real records. compatible retains each assignment whose two values satisfy every supplied premise. Missing information is represented by omitting a premise, not by passing False or None. Its result is conditional on the premises; the function does not verify their sources.

"""Offline logical illustration; no real records, probabilities or actions."""
from itertools import product

FIELDS = {"authorized": 0, "mismatch": 1}
WORLDS = tuple(product((False, True), repeat=2))


def compatible(premises):
    """Return assignments consistent with explicitly supplied toy premises."""
    if not isinstance(premises, (tuple, list)):
        raise ValueError("premises must be a sequence of field/Boolean pairs")
    checked = []
    for premise in premises:
        if not isinstance(premise, (tuple, list)) or len(premise) != 2:
            raise ValueError("each premise must contain a field and a Boolean")
        field, value = premise
        if not isinstance(field, str) or field not in FIELDS:
            raise ValueError("unknown proposition")
        if type(value) is not bool:
            raise ValueError("a premise value must be an actual Boolean")
        checked.append((FIELDS[field], value))
    return tuple(w for w in WORLDS
                 if all(w[index] == value for index, value in checked))


def report(label, premises):
    states = compatible(premises)
    text = " ".join(f"A{int(a)}M{int(m)}" for a, m in states)
    print(f"{label}: {text or 'INCONSISTENT_PREMISES'}")


cases = (
    ("NO_PREMISES", ()),
    ("AUTH_ONLY", (("authorized", True),)),
    ("MISMATCH_ONLY", (("mismatch", True),)),
    ("AUTH_AND_MISMATCH", (("authorized", True), ("mismatch", True))),
    ("AUTH_TWICE", (("authorized", True), ("authorized", True))),
    ("NO_AUTH_BUT_MATCH", (("authorized", False), ("mismatch", False))),
    ("CONFLICT", (("authorized", True), ("authorized", False))),
)
for label, premises in cases:
    report(label, premises)

for label, premises in (
    ("STRING_FALSE", (("authorized", "False"),)),
    ("MISSING_NOT_FALSE", (("mismatch", None),)),
    ("UNMODELED_ACTOR", (("operator_is_person_x", True),)),
):
    try:
        compatible(premises)
    except ValueError:
        print(f"{label}: INVALID_PREMISE")
    else:
        raise AssertionError(f"{label} was incorrectly accepted")

Reproduced output:

NO_PREMISES: A0M0 A0M1 A1M0 A1M1
AUTH_ONLY: A1M0 A1M1
MISMATCH_ONLY: A0M1 A1M1
AUTH_AND_MISMATCH: A1M1
AUTH_TWICE: A1M0 A1M1
NO_AUTH_BUT_MATCH: A0M0
CONFLICT: INCONSISTENT_PREMISES
STRING_FALSE: INVALID_PREMISE
MISSING_NOT_FALSE: INVALID_PREMISE
UNMODELED_ACTOR: INVALID_PREMISE

Read the first six lines as a worked elimination argument. With no premises, all four assignments remain possible within the model. Authorization alone leaves both a matching and a mismatching service state. A mismatch alone leaves both authorization possibilities. Combining the two positive premises selects A1M1, but uniqueness within two modeled propositions is not unique causation. Repeating the authorization premise leaves the same two assignments; it does not answer the service question. A0M0 illustrates why a correct-looking result cannot supply missing authority.

The program does not model source reliability or statistical corroboration. Two genuinely independent observations can affect a real analyst's confidence in a proposition, but counting repeated filters here does not measure that change. Before describing two reports as independent, check whether they merely repeat one underlying observation. Do not convert the number of remaining assignments into an incident probability.

Adversarial interpretation. The conflicting pair says both A and not-A for what is supposed to be the same scoped proposition. Zero remaining assignments therefore means that the entered premises cannot all hold together under that interpretation. It does not establish that the service is safe, that an incident is impossible, or which source is wrong. Check for different objects, operation times, policy versions, scope, transcription mistakes or a genuine contradiction; retain both reports while the disagreement is resolved. A later authorization cannot silently replace the authorization applicable to the earlier operation.

The final three controls reject a textual Boolean, an unknown value presented as a finding, and an actor proposition that the model does not contain. Rejecting None is not a reason to replace it with False: omit that unresolved premise and report the missing check. This small program has no way to establish human identity, so an operator_is_person_x input must not be accepted as if it followed from A or M.

Apply the distinction to the existing EX014, without changing its format

At 09:05, use only R1–R8. At 09:20, the release permits R1–R16, but release does not turn every proposition into a known Boolean. R9 reports the B19 edit and R10 omits B19 from the supplied plan. That omission is a reason to investigate the operation's authorization, not a complete determination of A0. R11 is a patron report, not the authoritative observed-versus-accepted comparison required to establish M1. Neither checkpoint supplies the completed checks used in the independent X21 example.

For EX014, retain the same two-row table, existing required fields and 0–2 rubric. Write the two discriminating questions yourself before consulting the final EX014 answer. One row should specify the bounded owner/source check needed to resolve the operation's task authorization; the other should specify the bounded comparison needed to assess the booking state. In each row, identify a possible answer that would change that particular judgment and the causal or human-actor claim it would still leave unproved. A proposed check is not a completed result. The existing final worked answer remains the comparison point; this worked example adds no numbered exercise and does not replace that answer.

A useful self-check is to cover one row: can the remaining row actually answer both questions? An authorization result cannot substitute for a state comparison. A state comparison cannot retrospectively authorize an operation. The same discipline applies to the next TRI2 reassessment: R14's incomplete quiet-query summary is not a general negative result, and R15's 24-hour policy still has no confirmed deletion anchor. Answering A or M does not supply the missing coverage, retention anchor, collection, hold or containment authority.

Source boundary. NIST SP 800-61 Revision 3 places incident response recommendations within cybersecurity risk management. The A/M notation, four-assignment illustration, Python program and EX014 teaching connection here are original OpenCyber Academy teaching material, not a NIST classification, prescribed severity matrix or automatic response policy. The supplied case records and the existing exercise's release boundaries control what may be claimed about Harbor Library.

2.2a Reassessing triage priority under changing evidence

2.2a.1 Why a priority decision must be revisable

The existing §2.2 distinguishes potential harm, time sensitivity, and confidence using a small qualitative matrix. That is a sound starting point, but an analyst needs to know how to revisit all three dimensions when information changes. A record can have an earlier event timestamp yet arrive later. A new source may support a previously uncertain fact while leaving its cause unknown. Evidence can become harder to preserve even while there is no proof of an attacker. A service owner may verify a booking-state mismatch without establishing who caused it.

Priority should therefore be written as a checkpoint-specific recommendation, not as a permanent label attached to a ticket. A useful note tells the incident lead what is concerning now, what can be lost through delay, which important claims remain weak, who owns the next authorized check, and which observation would require reassessment.

A provisional priority is not the answer to "Did a cyberattack occur?" It is an answer to "Which bounded response work deserves attention next, given the evidence and operational risk available at this checkpoint?" The answer can remain time-sensitive without attributing malicious conduct, and it can be revised without concealing the earlier reasoning.

NIST SP 800-61 Revision 3 explains incident response as part of cybersecurity risk management and emphasizes organizational context. NIST CSF 2.0 provides flexible high-level outcomes for understanding, assessing, prioritizing and communicating cybersecurity risks. Neither establishes the specific categories, labels or decision thresholds in this exercise. TRI2 is an original OpenCyber Academy method for teaching bounded reassessment, not a NIST-defined incident classification or automatic containment policy.

2.2a.2 First freeze what the analyst actually knows

Before comparing possible harm or urgency, identify the decision checkpoint and released sources. Otherwise an apparently precise priority record may rely on information the earlier responder did not have.

At 09:05 the learner has R1–R8: R1 reports the desk-a sign-in, R3/R4 report B17/B18 edits, R5 records a patron concern, and R6 reports scheduled training. R7 is a backup-job status and R8 a network summary without destination detail. These records support investigating the booking changes and training explanation. They do not prove unauthorized account control, restore success, information transfer, or a broad population impact.

At 09:20 R9–R16 become available. R9 reports a B19 edit whose event time was 09:03, R10 lists B17/B18 in the supplied plan but not B19, R11 records a second patron report, R12 provides a staff recollection, R13 a limited identity summary, R14 an application-audit quiet-query summary, R15 a 24-hour deletion notice without a known anchor, and R16 a service-owner request for impact assessment before account change.

The correct interpretation is not that the Round A analyst ignored R9. The learner did not receive it until Round B. In the review ledger, preserve reported event time, learner availability time, and decision time as different fields. A reviewer should be able to reconstruct the 09:05 decision from only the then-released records.

The TRI2 code checks this boundary. A deliberate attempt to cite R9 during Round A is rejected before any interpretation or priority discussion occurs. That output represents a classroom release rule; it is not a statement that the underlying event did not occur.

2.2a.3 Confidence is not one number

A single "confidence: medium" label mixes several logically independent questions. A beginner can reason more accurately by writing three separate fields:

  1. Confidence in the record as supplied: Is the named item present in the released classroom packet, with a source and stated content? In a live case this would additionally require relevant source authentication and collection/provenance review.
  2. Confidence in interpretation: Does the record support the asserted business or security meaning? A booking edit may be recorded accurately while its authorization remains disputed.
  3. Confidence in cause and attribution: Does available evidence connect the operation to a specific causal mechanism or person? A staff account label or nearby sign-in time is not human attribution.

These fields can move differently. At 09:20 the record of the B19 edit is now available; the interpretation that it was unauthorized remains unsettled because a training-plan omission is not proof that no other approved instruction existed; and the identity of the operator is not established by R13's limited summary.

A useful report states these as separate sentences:

"R9 is a supplied synthetic booking-audit record describing a B19 change. The approved scope of that operation remains unresolved because the supplied plan lists B17/B18 but not B19. The human controlling the session is not established."

This is more useful than labeling the entire case "high confidence" without saying confidence in what. It also prevents another error: downgrading every field just because attribution is weak. A patron-facing booking problem can require action even when the cause is unknown.

TRI2 uses descriptive statuses rather than calibrated probabilities. The code cannot authenticate any real source or measure an actual probability of compromise, and its labels must not be interpreted as a universal confidence scale.

2.2a.4 A record can be reliable while the hypothesis remains weak

An analyst sometimes treats independent evidence as a vote: three records agree, therefore the cause is established. But agreement depends on what proposition the sources actually address. R9 describes an application edit; R10 is a training-plan document; R12 is a staff recollection. These do not measure the same fact. Their differing contents can legitimately strengthen the need for an authorization check without proving account theft.

Consider three statements:

The first two follow from the records' stated contents. The third is a causal attribution and does not follow automatically. An incomplete training plan or forgotten approved task remains a possible explanation; neither alternative is itself confirmed.

Conflicting signals should increase attention to the discriminator, not force the team to choose one story immediately. The training owner might provide a dated, approved addendum; a source custodian might supply reliable session-to-request mapping; the service owner might confirm a current booking mismatch. Each has a different purpose.

A full, owner-confirmed task list that includes a particular booking could reduce concern that that booking's task was outside the plan. It still does not prove the human operator, establish every booking state, or resolve evidence-retention risk. A separate valid observation is required for each stronger claim.

2.2a.5 Potential harm and verified impact are different dimensions

A patron report is evidence of a reported problem, not automatic proof of an incorrect authoritative reservation value. At 09:05, R5 records a concern about B18. At 09:20, R11 adds a B19 concern, but neither record provides an owner-approved comparison of current versus expected booking values.

A potential-harm note asks which business function could be affected if the concern is accurate: patron scheduling, room availability, front-desk integrity or continuity of reservations. It may require quick attention when the full impact is unknown.

A verified-impact note requires a different type of source: an authorized current-state observation compared with an accepted target, with scope and provenance adequate for the question. Its conclusion is still limited to the objects actually checked. It does not magically establish how many people were affected.

The IM2 laboratory already teaches that report counts, event counts, booking IDs and people are distinct units, and that a biased subset cannot estimate the whole population. TRI2 does not recalculate those counts. It teaches what happens to a priority recommendation when a bounded impact check is actually supplied or remains missing.

For example, a future independent synthetic variant in which an owner-approved comparison confirms a mismatch can justify revisiting potential business harm. That observation cannot be inserted into canonical R1–R16, which contains no completed authoritative state comparison.

At both checkpoints, the correct discipline is to state: potential consequences are being assessed; measured impact remains unknown unless an appropriate source has actually verified it.

2.2a.6 Split time sensitivity into preservation and containment concerns

The original matrix places multiple urgent issues in one time-sensitivity column. For decision making, it helps to track two independent urgency questions.

Preservation urgency concerns whether a decision-relevant source may become unavailable if the team waits. It depends on source retention, availability, owner response, collection authority and any verified preservation control. The clock can matter even if the initial interpretation is wrong.

Containment urgency concerns whether additional harmful activity is sufficiently plausible or observed to justify prompt consideration of protective options. It depends on signs of continued activity, current impact, confidence in control scope and possible damage from the intervention itself. A short log-retention period does not, by itself, prove a new harmful edit or authorize account restriction.

At 09:20, R15 supplies a reported policy for detailed booking-audit records: a 24-hour duration with an authorized-copy provision. It does not demonstrate the policy's enforcement, its starting anchor, the oldest retrievable relevant row, or an actual deletion. The time-sensitive task is to ask the incident lead and application custodian about a bounded preservation route and current source availability. Do not add 24 hours to R9's event time or treat the 09:14 notice time as a new retention start. A retained copy, if later authorized and actually acquired, would have its own custody and retention record; its existence would not by itself show that deletion at the producing source stopped. Preservation concern is therefore justified without a confirmed loss outcome or a conclusion that an attack occurred.

At that same checkpoint, R9 and R11 merit investigation and continued review, but there is no post-09:20 newly verified edit in the released record. R14's quiet result is limited to an unspecified query range, and cannot establish that no additional activity occurred. The incident lead therefore needs a review trigger and a plan for authorized escalation without describing a containment operation as already needed or performed.

A team may honestly say that evidence-preservation contact is time-sensitive while its cause attribution remains low or unproven. Those statements do not conflict.

2.2a.7 A quiet query is not a negative observation until its scope is understood

A common reassessment error is:

"The dashboard shows no new alerts, so lower the priority."

That may be reasonable only after the observer establishes what the query measured. For R14, the supplied card says no further booking edits appear after R9 within the queried range. It does not provide all the source, account, time, ingestion and completeness details needed for a universal absence claim.

A query might use an interval that excludes the time of interest, an account filter that excludes another path, a source that does not include the relevant operation, or a log store that has not finished ingesting records. The absence of a returned row is therefore narrower than "no new edit occurred."

VQ2 already teaches query-envelope conditions, and T2 demonstrates delayed arrival. The present lesson does not duplicate those mechanics. It asks what a response lead should do with an imperfect negative observation: leave the containment concern open for review and request the missing query manifest or better-bounded observation through the authorized custodian.

A valid negative result can narrow the concern for a specified tested path and interval. Even then, it cannot prove the absence of malicious behavior on every route, and it does not establish human attribution or solve a preservation problem. The reduction must be scoped to the proposition that the negative test could actually refute.

TRI2 emits QUIET_NOT_NEGATIVE_PROOF when a case relies on an apparently quiet summary whose coverage is not established. That warning does not claim that a new edit occurred; it prevents the unsupported opposite conclusion.

2.2a.8 How to write an evidence-changing review checkpoint

A useful priority note is not merely a timestamp followed by a color. It should answer these seven questions:

Lesson table; scroll horizontally to read all columns →

Question What belongs in the provisional note
What was available? Checkpoint and exact released source IDs
What could be harmed? User/service outcome and scope, marked potential or verified
What does each confidence field mean? Record availability, event interpretation and cause/attribution separately
What could be lost through waiting? Source retention or continuing-activity concern, with explicit missing anchors
What is proposed next? One bounded, authorized information request or decision review, not automatic execution
Who owns it? Incident lead, service owner and/or relevant custodian according to the request
When must it change? Named review time or an observable event that triggers earlier reassessment

For Round A, a classroom answer may favor prompt review with the training owner and a 09:15 checkpoint, without concluding compromise. This is justified by R1–R8's booking changes, user report and scheduled training. It is not a claim that the organization adopted a numbered severity category.

For Round B, the incident lead can keep a prompt, bounded review at 09:35 or earlier on a verified new unexplained edit or material source-availability concern. The newly released B19 edit increases the need for explanation; R15 separately raises preservation urgency. The next proposed step may involve asking the application custodian for the retention anchor and feasibility of P-A, plus a service-owner-approved impact check.

The note should distinguish "will request" from "requested," "approved," "collected" and "verified." None of those latter states is supplied for a real Round B collection or hold. An attractive priority label cannot substitute for authorization.

2.2a.9 Example one — correct 09:05 assessment without hindsight

Correct record, canonical Round A only:

"At 09:05 UTC, Harbor Library's supplied R1–R8 records report a desk-a sign-in, booking edits to B17/B18 and a patron concern about B18. R6 records scheduled training but does not show that those particular edits were approved. The potential service consequence warrants prompt review; there is no verified broad impact, human attribution or demonstrated ongoing attack. The incident lead proposes asking the training owner for the approved task list and preserving the current synthetic records within the classroom scope, with a review at 09:15 or sooner if a separately verified new unexplained edit is supplied."

This note has a specific checkpoint, source set, service concern, interpretation limit, proposed authorized next question, owner and review. Its confidence in the case records being supplied is not the same as confidence that the changes were unauthorized. R7's completed backup job and R8's limited network summary do not settle the cause.

A reviewer must not say that the 09:05 lead already knew B19 was changed at 09:03. R9 is released only at 09:20, and its event timestamp cannot backdate its availability. A retrospective discussion can identify that later source as new information but must preserve the earlier decision's actual basis.

Neither the original note nor the comparison claims a real identity operation, account restriction, source copy or production log review. The phrase prompt review is a locally reasoned fictional priority description, not a NIST-prescribed severity grade.

2.2a.10 Example two — incorrect 09:20 downgrade

Incorrect synthetic Round B handoff:

"R14 shows no edits after B19, so the issue is over. R12 cannot remember B19, which proves a thief used desk-a; nevertheless the case can be marked low because no further alarms were seen. R15's 24 hours means we have until exactly 09:03 tomorrow to export the evidence, and R11 proves another patron's booking was corrupted."

This text simultaneously overstates a negative query, fabricates a human attribution, invents a retention anchor and equates a patron report with an authoritative business-state mismatch. It cannot support a defensible downgrade. The contradiction between "proved theft" and "case over" also shows why a single severity label is not useful without specific evidence claims.

Corrected bounded Round B assessment:

"At 09:20 UTC, R9 adds a B19 booking edit whose event time was 09:03; R10's supplied training plan names B17/B18 but not B19, while R12 is an unverified recollection about the action. The account operator, approval and root cause remain unresolved. R11 reports a patron concern but does not measure current booking correctness or population impact. R14 is a quiet summary with incomplete query coverage, and R15 reports a 24-hour source policy with no confirmed anchor. The incident lead keeps a prompt, scoped review, seeks an authorized application-custodian retention and query-scope clarification plus a service-owner impact comparison, and will reassess at 09:35 or earlier on a verified new edit or a substantiated source-loss risk."

The correction preserves each level of confidence separately. It treats the preservation question as urgent without claiming malicious cause and avoids selecting any production containment control. The report does not assert that a real source hold was applied. Its review time is a classroom decision checkpoint, not an operational requirement imposed by NIST.

A good learner can explain why the priority is provisional without saying "we have no confidence in anything." The source cards are available, the authorization hypothesis is contested and the actor is unknown; those are different propositions.

2.2a.11 Reassess sequentially when a new card arrives

A review must start with the previous frozen decision, not a blank slate. A useful sequence is:

  1. Freeze the prior note. Identify what the analyst could defend at the earlier checkpoint and which observations were missing.
  2. Declare the new release. Add only the cards actually supplied at the new checkpoint; preserve their event and availability times.
  3. List the changed claims. Did the new card add a source record, contradict an interpretation, verify a business mismatch, or merely describe another report?
  4. Check the three confidence fields separately. A new record may raise confidence in its content being supplied without establishing the meaning or actor.
  5. Re-evaluate potential versus verified harm. Name the legitimate business unit affected or still unmeasured.
  6. Re-evaluate preservation and containment urgency independently. Record the relevant custodian and authorized test for each.
  7. Write the next provisional note. Keep a decision owner, next review time/trigger, proposed authorized request and unresolved limitations.
  8. Version rather than overwrite. Save the 09:05 note and the 09:20 revision independently, with a short statement of what changed.

The change from Round A to Round B is not automatically an escalation in every dimension. It is a richer evidence set. R9 adds a specific booking-edit question and R15 introduces an evidence-availability concern that was not released earlier; the human actor remains unknown. A measured service mismatch still requires an approved comparison not present in R1–R16.

This process allows the team to be responsive without treating every new card as equal in significance. A retention notice may change how quickly the team contacts a custodian, while an owner-approved task addendum may change the interpretation of one booking. Neither must move every dimension in the same direction.

2.2a.12 Reassess on conflicting signals without forcing a verdict

Incident sources may pull the interpretation in different directions. R9 reports B19 edited, R10 omits B19 from the supplied plan and R12 records that the staff member does not remember B19. These facts create uncertainty about authorization. They do not independently prove account takeover, identity theft or deliberate action.

A beginner should avoid treating conflicting evidence as a mathematical vote. One high-quality source may be more directly relevant to a specific question than several weak contextual reports, but assessing quality requires scope, producer semantics, completeness, possible bias and timing. A staff recollection is context; a complete, dated approved plan would answer the task-scope question more directly, if it actually exists and is verified.

When sources conflict, write:

The proper output is often a specific unresolved hypothesis, not "medium confidence" in a generalized attack story. This is a valuable result because it makes the next verification work reproducible for another analyst.

The TRI2 CONFLICTING_SIGNALS variant models an incomplete approved-task addendum: the analyst cannot downgrade solely because a document is present if its scope or completeness is not verified.

2.2a.13 Reassess on increased preservation risk without claiming a breach

At 09:20, R15 reports a 24-hour policy for detailed booking-audit records, subject to an authorized copy. The record does not identify the event/ingestion/storage anchor, the exact deletion job, the oldest still-accessible relevant row, or a confirmed effective hold. That uncertainty is itself operationally meaningful.

The response team can request rapid custodian clarification and appropriate preservation authorization before proving an attack. Such a request protects the ability to test competing explanations later; it does not assert that the leading malicious explanation is true. Equally, the notice does not justify collecting unrelated patron information or using an unapproved account to change retention settings.

If the custodian is unavailable, the note should record the failed contact, the accountable incident lead, a designated approved escalation route and the remaining uncertainty. Do not turn the absence of a response into proof that data were deleted. Do not assume that filing a request applied a hold.

The existing HOLD2 chapter addition teaches how to distinguish source hold requests, approval, application and verified effect. TRI2 instead asks how that uncertainty affects the provisional urgency and next review, without replaying HOLD2's test or inventing a successful hold.

A useful phrase is: "Evidence-availability concern is time-sensitive; cause confidence remains unproven; exact deletion deadline unknown." Those three facts can all be true at the same checkpoint.

2.2a.14 A new verified edit is a conditional review trigger, not a command

An incident lead may specify that a future, source-verified unexplained booking edit should cause an immediate reevaluation of containment options. This does not mean such an edit has occurred after the 09:20 Round B review. It is a conditional threshold attached to the provisional decision.

A trigger must say what counts as verified for the specific purpose. At minimum, a relevant custodian would need to check the source, timestamp semantics, selected booking/object, possible approved task and source coverage. The team would then bring the result to the incident lead and service owner for a decision.

The trigger does not authorize an automatic account disable, pause or data collection outside scope. Choosing a containment action requires its own risk and authorization assessment, and the later Module 3 comparison is outside the two checkpoints here.

TRI2 includes a fictional NEW_EDIT_VERIFIED input as an independent scenario. The code marks it variant-only; an attempt to insert the same flag into a canonical snapshot is explicitly rejected. This guards against a common learning error: performing an interesting counterfactual and then treating its event as if it had appeared in the R-series evidence.

The proper human question after a verified new edit is which intervention, if any, would now be proportionate, given potential harm and business costs. The toy classifier deliberately does not make that selection.

2.2a.15 A justified downgrade must specify which concern decreased

"Priority lowered" is not a complete assessment. Which aspect became less concerning, what new evidence justified that change, and what remained unresolved?

Suppose a separately labeled future synthetic variant supplies an owner-approved, complete training addendum explicitly authorizing the B19 task and a properly bounded, complete quiet observation for a specified subsequent interval. Those inputs could reduce concern that B19 was outside the approved training scope, while still leaving the human operator unproven. They cannot establish that every booking is correct or that no other activity occurred outside the tested interval.

The preservation question also remains separate. If detailed records still face an unknown retention anchor and no effective source safeguard has been independently substantiated, the team should not abandon preservation simply because the B19 authorization hypothesis became less concerning.

Another variant might include a separately verified and approved preservation safeguard, reducing source-loss concern within its tested source scope. Even then the team should not call the entire incident resolved. The conclusion is a limited change in one concern, not a global proof of safety.

TRI2 provides two contrasting downgrade cases: one with a declared synthetic source safeguard, and one with the same task-scope evidence but source preservation still unconfirmed. Both may show reduced B19 authorization concern; only the first prints a fixture-only protected-source status. Neither automatically assigns a lower NIST severity, executes containment or closes the case.

A downgrade based solely on R14's quiet summary with unknown filters is unjustified. A contradiction in a source should be resolved by obtaining stronger evidence, not by choosing whichever report supports the desired lower priority.

2.2a.16 What the model can and cannot classify

The TRI2 script deliberately prints descriptive evidence states, not a numeric severity, risk score, attack probability or action recommendation. Its six columns mean:

Lesson table; scroll horizontally to read all columns →

Printed prefix Interpretation
R Record availability inside the supplied classroom set; not authenticated production source provenance
I Support for the current event interpretation or task-scope explanation
C Whether a human actor has been established; it remains unproven here
H Reported potential harm versus a separately asserted bounded verified business observation
P Evidence-preservation concern, source contact or declared synthetic safeguard
K Whether the concern about continuing or out-of-scope activity deserves another human review

The flags field lists important reasoning barriers such as a quiet query without known coverage, an unsupported attribution claim or a requested downgrade not justified by the supplied conditions.

None of these values authorizes a control. A result such as K:NEW_EDIT_REVIEW asks for human reevaluation; it is not a request to disable an account. A result such as P:SOURCE_PROTECTED_IN_VARIANT means the invented Boolean input claimed a safeguard, not that the code independently verified a source hold.

The code first checks the evidence release boundary. If a Round A packet references R9, the entire assessment stops with BLOCK_UNRELEASED_SOURCE. It also rejects invented canonical events or impact, a claimed negative result without known query coverage, and a case-wide downgrade while a new edit remains open. Even in a labeled variant, a complete approved addendum must explicitly name B19 before the B19 task-scope concern can narrow. These are input-consistency checks over declared premises, not independent verification of those premises.

The source and event confidence categories do not attempt to measure real probabilities. A real organization would need its own criteria, evidence quality controls, authorized response plan and responsible decision makers. For the rapid course, the aim is to make unsupported claims and reassessment points visible.

2.2a.17 Versioned priority notes prevent hindsight revision

When the team receives new information, preserve the old decision rather than silently replacing it. A minimal revision log might contain:

Lesson table; scroll horizontally to read all columns →

Field Round A note Round B update
Evidence boundary R1–R8 by 09:05 R1–R16 by 09:20
Relevant unresolved operation B17/B18 authorization B19 authorization added
Service impact Patron report, no verified comparison Second patron report, still no authoritative comparison
Source availability concern No R15 rule released R15 retention notice; anchor unknown
Interpretation Scheduled training plausible but unproved Training task scope more specifically contested for B19
Cause attribution Unproved Still unproved
Next requested work Training-owner clarification B19 approval check, bounded impact and source-retention clarification
Owner and review Incident lead; 09:15 or verified trigger Incident lead; 09:35 or earlier verified trigger

These descriptions do not claim that no loss risk existed before R15 was released; they say the learner had not yet received that notice. The distinction matters when reviewing whether an earlier decision was reasonable from the information then available.

A revision should identify which evidence caused the change. "Updated priority after new information" is vague. "Added R9/R10/R15 to the permitted set at 09:20; separated unresolved B19 authorization from preservation contact concerning R15" is testable.

A reviewer should also ask whether the new priority note still includes the original bounded approval constraints. A source becoming more urgent does not dissolve the need for permission, and a new suspicion does not identify an attacker.

2.2a.18 Section synthesis: urgency and certainty can move independently

The Module 2 triage skill is not selecting the most alarming label. It is writing a provisional decision note that distinguishes what the records say, what those events mean, who or what caused them, what harm could follow, what harm has been verified, which evidence may disappear, and whether continuing activity requires urgent owner review.

At 09:05, R1–R8 permit a prompt assessment, training-owner question and bounded review. At 09:20, R9–R16 add a B19 authorization question, a second patron report, an imperfect quiet-query summary and a short-retention notice with no confirmed anchor. Preservation concern can rise while cause attribution remains unknown.

A valid reclassification is limited to the evidence that supports it. A wrong-source negative query cannot lower concern about unseen activity. An approved task addendum may narrow the B19 explanation without proving the human actor. A custodian who cannot be reached increases uncertainty about evidence availability but does not prove evidence loss. A verified new edit would demand fresh human attention but would not automatically select a control.

The following offline lab uses these principles as structural review labels. It does not classify a real incident, make an operational decision, or extend any canonical R-series record.


2.2b TRI2 offline laboratory — reassessment under incomplete evidence

Classification: Independent teaching-only fixture. The labels TRI2 and its Boolean variant fields are not original Harbor Library records, measured operational outcomes, or accepted source-hold receipts.

Goal: Show why different dimensions change independently when evidence becomes available; identify unsupported negative-query, attribution, harm and downgrade claims; preserve the Round A/Round B release boundary; and show which human review question remains open.

Environment: Python 3 standard library, entirely in memory; no files, network access, accounts, collection, retention-control changes, notifications or operational actions.

Important model contract: The R:SUPPLIED_NOT_AUTHENTICATED output means a synthetic case card is present for the learner. It does not mean a producer's log has been independently authenticated. H:VERIFIED_BOUNDED and P:SOURCE_PROTECTED_IN_VARIANT are assertions made inside invented variant inputs; the program does not actually conduct an impact comparison or source-hold test. C:ACTOR_UNPROVEN always remains true because no simulated human attribution is established.

Input and predictive exercise

Before executing, predict why R9 is unavailable at 09:05, why R14's quiet summary cannot prove no more edits, why an R15 loss concern cannot become a deletion deadline, and why a valid addendum might justify a narrow B19 explanation reassessment without ending preservation work. Then execute the exact program.

Full Python 3 code
from copy import deepcopy

RELEASE = {
    "A": {f"R{i}" for i in range(1, 9)},
    "B": {f"R{i}" for i in range(1, 17)},
}

BASE = dict(
    stage="B",
    refs=("R9", "R10", "R11", "R12", "R13", "R14", "R15", "R16"),
    record_provided=True,
    owner_state_check=False,
    patron_report=True,
    quiet_summary=True,
    query_coverage_known=False,
    retention_notice=True,
    retention_anchor_known=False,
    source_safeguard_confirmed=False,
    custodian_available=True,
    verified_fresh_edit=False,
    approved_addendum=False,
    training_scope_verified_complete=False,
    addendum_names_b19=False,
    no_new_edits_verified=False,
    claim_human_actor=False,
    request_downgrade=False,
    variant=False,
)

def validate_tri2_input(s):
    """Validate this classroom packet, not a real evidence source."""
    if not isinstance(s, dict) or set(s) != set(BASE):
        return "BLOCK_INPUT_SCHEMA"
    if not isinstance(s["stage"], str) or s["stage"] not in RELEASE:
        return "BLOCK_UNKNOWN_STAGE"
    refs = s["refs"]
    if not isinstance(refs, (tuple, list)):
        return "BLOCK_SOURCE_LIST_TYPE"
    if not refs:
        return "BLOCK_EMPTY_SOURCE_SET"
    if any(not isinstance(ref, str) for ref in refs):
        return "BLOCK_SOURCE_ID_TYPE"
    if len(set(refs)) != len(refs):
        return "BLOCK_DUPLICATE_SOURCE_ID"
    for field in BASE:
        if field not in ("stage", "refs") and type(s[field]) is not bool:
            return "BLOCK_NON_BOOLEAN:" + field
    return None

def review(s):
    problem = validate_tri2_input(s)
    if problem is not None:
        return problem
    if not set(s["refs"]).issubset(RELEASE[s["stage"]]):
        return "BLOCK_UNRELEASED_SOURCE"
    # Declared flags must be supported by cards in this release set.
    released_refs = set(s["refs"])
    if s["retention_notice"] and "R15" not in released_refs:
        return "BLOCK_UNRELEASED_CLAIM:R15"
    if s["quiet_summary"] and "R14" not in released_refs:
        return "BLOCK_UNRELEASED_CLAIM:R14"
    if s["patron_report"] and not ({"R5", "R11"} & released_refs):
        return "BLOCK_UNRELEASED_CLAIM:PATRON_REPORT"
    if (
        s["approved_addendum"] or s["training_scope_verified_complete"]
        or s["addendum_names_b19"]
    ) and not {"R9", "R10"}.issubset(released_refs):
        return "BLOCK_UNRELEASED_CLAIM:R9_R10"
    if s["verified_fresh_edit"] and not s["variant"]:
        return "BLOCK_INVENTED_CANONICAL_EVENT"
    if not s["variant"] and any((
        s["owner_state_check"], s["source_safeguard_confirmed"],
        s["approved_addendum"], s["no_new_edits_verified"],
        s["query_coverage_known"], s["retention_anchor_known"],
        s["training_scope_verified_complete"], s["addendum_names_b19"],
        not s["custodian_available"],
    )):
        return "BLOCK_INVENTED_CANONICAL_PREMISE"
    if s["no_new_edits_verified"] and not s["query_coverage_known"]:
        return "BLOCK_UNBOUNDED_NEGATIVE_PREMISE"
    if s["verified_fresh_edit"] and s["no_new_edits_verified"]:
        return "BLOCK_CONFLICTING_ACTIVITY_PREMISES"
    if s["verified_fresh_edit"] and s["request_downgrade"]:
        return "BLOCK_GLOBAL_DOWNGRADE_NEW_EDIT_OPEN"

    record = (
        "R:SUPPLIED_NOT_AUTHENTICATED"
        if s["record_provided"] else "R:UNVERIFIED"
    )
    interpretation = (
        "I:TRAINING_SCOPE_SUPPORTED"
        if s["approved_addendum"]
        and s["training_scope_verified_complete"]
        and s["addendum_names_b19"]
        else "I:CONTESTED_OR_INCOMPLETE"
    )
    cause = "C:ACTOR_UNPROVEN"

    harm = (
        "H:VERIFIED_BOUNDED"
        if s["owner_state_check"]
        else "H:REPORT_ONLY" if s["patron_report"]
        else "H:NOT_MEASURED"
    )
    preserve = (
        "P:SOURCE_CONTACT_BLOCKED"
        if s["retention_notice"]
        and not s["custodian_available"]
        else "P:SOURCE_PROTECTED_IN_VARIANT"
        if s["source_safeguard_confirmed"]
        else "P:ANCHOR_UNKNOWN_CONTACT"
        if s["retention_notice"]
        and not s["retention_anchor_known"]
        else "P:RETENTION_NEEDS_CHECK"
        if s["retention_notice"]
        else "P:NO_RELEASED_LOSS_RULE"
    )
    contained = (
        "K:NEW_EDIT_REVIEW"
        if s["verified_fresh_edit"]
        else "K:B19_CONCERN_REDUCED"
        if s["approved_addendum"]
        and s["training_scope_verified_complete"]
        and s["addendum_names_b19"]
        and s["no_new_edits_verified"]
        else "K:OPEN_HYPOTHESES"
    )

    flags = []
    if s["quiet_summary"] and not s["query_coverage_known"]:
        flags.append("QUIET_NOT_NEGATIVE_PROOF")
    if s["claim_human_actor"]:
        flags.append("ATTRIBUTION_CLAIM_UNSUPPORTED")
    if s["request_downgrade"]:
        if (
            s["approved_addendum"]
            and s["training_scope_verified_complete"]
            and s["addendum_names_b19"]
            and s["no_new_edits_verified"]
        ):
            flags.append("DOWNGRADE_ONLY_B19_TASK_SCOPE_CONCERN")
        else:
            flags.append("DOWNGRADE_NOT_JUSTIFIED")
    if s["retention_notice"] and not s["custodian_available"]:
        flags.append("ESCALATE_CONTACT_NO_HOLD_ASSUMED")
    if s["verified_fresh_edit"]:
        flags.append("REVIEW_ON_NEW_VERIFIED_SIGNAL")

    return " ".join([
        record, interpretation, cause, harm, preserve,
        contained,
        "flags=" + (",".join(flags) if flags else "NONE"),
    ])

def case(**changes):
    s = deepcopy(BASE)
    s.update(changes)
    return review(s)

ROUND_A = deepcopy(BASE)
ROUND_A.update(
    stage="A",
    refs=("R1", "R3", "R4", "R5", "R6"),
    retention_notice=False,
    quiet_summary=False,
)

CASES = [
    ("ROUND_A_0905", ROUND_A),
    ("ROUND_B_0920", BASE),
    ("UNRELEASED_R9_AT_A",
     {**ROUND_A, "refs": ("R1", "R9")}),
    ("QUERY_FILTER_UNKNOWN", {"variant": True}),
    ("RETENTION_ANCHOR_UNKNOWN",
     {"variant": True, "quiet_summary": False}),
    ("PATRON_REPORT_UNVERIFIED",
     {"variant": True, "quiet_summary": False,
      "retention_notice": False}),
    ("CLAIM_FALSE_ATTRIBUTION",
     {"variant": True, "claim_human_actor": True}),
    ("CUSTODIAN_UNAVAILABLE",
     {"variant": True, "custodian_available": False}),
    ("NEW_EDIT_VERIFIED",
     {"variant": True, "verified_fresh_edit": True}),
    ("NEW_EDIT_NOT_CANONICAL",
     {"verified_fresh_edit": True}),
    ("CONFLICTING_SIGNALS",
     {"variant": True, "approved_addendum": True,
      "training_scope_verified_complete": False}),
    ("PREMATURE_DOWNGRADE",
     {"variant": True, "request_downgrade": True}),
    ("ADDENDUM_OMITS_B19",
     {"variant": True, "request_downgrade": True,
      "approved_addendum": True,
      "training_scope_verified_complete": True,
      "no_new_edits_verified": True,
      "query_coverage_known": True}),
    ("UNBOUNDED_NEGATIVE_PREMISE",
     {"variant": True, "no_new_edits_verified": True}),
    ("INVENTED_CANONICAL_IMPACT",
     {"owner_state_check": True}),
    ("NEW_EDIT_AND_GLOBAL_DOWNGRADE",
     {"variant": True, "verified_fresh_edit": True,
      "request_downgrade": True}),
    ("VALID_B19_TASK_SCOPE_DOWNGRADE",
     {"variant": True, "request_downgrade": True,
      "approved_addendum": True,
      "training_scope_verified_complete": True,
      "addendum_names_b19": True,
      "no_new_edits_verified": True,
      "query_coverage_known": True,
      "source_safeguard_confirmed": True}),
    ("DOWNGRADE_WITH_PRESERVATION_OPEN",
     {"variant": True, "request_downgrade": True,
      "approved_addendum": True,
      "training_scope_verified_complete": True,
      "addendum_names_b19": True,
      "no_new_edits_verified": True,
      "query_coverage_known": True,
      "source_safeguard_confirmed": False}),
    ("OWNER_VERIFIED_IMPACT",
     {"variant": True, "owner_state_check": True}),
    ("SOURCE_SAFEGUARDED_IN_VARIANT",
     {"variant": True,
      "source_safeguard_confirmed": True}),
]

for label, values in CASES:
    result = review(values) if "stage" in values else case(**values)
    print(label + " " + result)

assert review(ROUND_A).startswith("R:SUPPLIED")
assert case(verified_fresh_edit=True) == (
    "BLOCK_INVENTED_CANONICAL_EVENT"
)

controls = [
    ("EMPTY_SOURCE_SET", {**ROUND_A, "refs": (), "patron_report": False}),
    ("TEXT_FALSE", {**BASE, "variant": "False", "owner_state_check": True}),
    ("CANONICAL_ANCHOR", {**BASE, "retention_anchor_known": True}),
    ("CANONICAL_COVERAGE", {**BASE, "query_coverage_known": True}),
    ("CANONICAL_CONTACT", {**BASE, "custodian_available": False}),
]
for label, sample in controls:
    print(label + " " + review(sample))

For the executed output, scenario interpretation and negative controls, read the TRI2 worked result in the final answer appendix. Predict the labels before opening the result.

Same-scope claims: a quiet summary is not verified absence

A contradiction in this exercise means asserting both that at least one qualifying event exists and that no qualifying event exists in the same bounded set. Two reports that look different are not necessarily contradictory. Before supplying verified_fresh_edit=True or no_new_edits_verified=True, freeze the object set, account and event predicate, source-coverage universe, time basis and half-open observation interval in a scope note. Within one TRI2 input, both flags refer to that same scope. A false flag means its positive assertion is not supplied; it does not prove the opposite. The program receives no scope note, manifest or timestamps and cannot verify them.

Worked synthetic example, separate from R1–R16. On one fictional day in UTC, consider edits to invented booking X31 under account Q7 during [10:00, 10:10). A declared verified finding places a matching edit at 10:04. A second declared finding claims complete coverage and no matching edits in that same interval. The 10:04 event is a counterexample to the absence claim. Both positive assertions cannot describe the same set consistently. The new guard returns BLOCK_CONFLICTING_ACTIVITY_PREMISES, preserving the disagreement for investigation rather than producing a combined assessment.

Now change only the absence claim's interval to [10:05, 10:10). The 10:04 event falls outside that later interval, so the claims need not conflict. Keep two scoped notes instead of forcing their flags into one TRI2 input. A half-open interval includes its starting instant and excludes its ending instant. An absence finding for a later interval does not erase the earlier event, identify an operator or close an incident.

The original fresh-edit branch already kept K:NEW_EDIT_REVIEW when a fresh edit was asserted. That conservative output did not make an accompanying same-scope absence assertion consistent. The added check rejects the inconsistent combination before a multidimensional result is printed. It does not change what supports I:TRAINING_SCOPE_SUPPORTED: that separate authorization label still requires the three applicable addendum premises. An earlier guard rejects an absence assertion without declared query coverage as BLOCK_UNBOUNDED_NEGATIVE_PREMISE; when both defects occur, that earlier block is reported first. These labels identify a classroom input problem, not a command to stop a service, delete evidence or choose which source is truthful.

quiet_summary=True is different again: it says a summary was supplied, not that a completed, adequately scoped absence check was verified. It can coexist with a fresh-edit assertion without becoming this contradiction. The original R14 summary remains incomplete evidence; the new check neither authenticates it nor expands its coverage.

Independent practice. Predict all four results below, then write two sentences explaining why the first X31 pair conflicts but the later-window pair need not. Run the complete TRI2 program first, followed by this separate continuation in the same fresh Python session. It uses only copied in-memory synthetic inputs and prints every assessment field, not merely the activity label.

# Run after the complete TRI2 listing in the same fresh Python session.
# These flags refer to one declared object/predicate/coverage/time scope.
same_scope = {
    **BASE, "variant": True, "verified_fresh_edit": True,
    "no_new_edits_verified": True, "query_coverage_known": True,
}
fresh_only = {**same_scope, "no_new_edits_verified": False}
absence_only = {**same_scope, "verified_fresh_edit": False}
quiet_with_fresh = {**BASE, "variant": True, "verified_fresh_edit": True}

for label, sample in (
    ("SAME_SCOPE_CONFLICT", same_scope),
    ("FRESH_ONLY", fresh_only),
    ("ABSENCE_WITHOUT_ADDENDUM", absence_only),
    ("QUIET_SUMMARY_WITH_FRESH", quiet_with_fresh),
):
    print(label + " " + review(sample))

Compare your predictions and interval explanation with the worked scope answer in the final appendix. The complete program still prints its original 25 lines; this continuation prints four additional lines. Retain the three release challenges below. This practice uses the existing supplementary lab rubric's evidence-boundary criterion; it changes neither EX014's two-row table, EX016's short note, EX024's two hypothetical pairs nor the 50-exercise denominator.

Release-guard challenge: After the baseline program, test three explicitly synthetic Round A variants: set retention_notice=True without R15, set quiet_summary=True without R14, and set variant=True, approved_addendum=True, training_scope_verified_complete=True, addendum_names_b19=True, no_new_edits_verified=True and query_coverage_known=True without R9/R10. The three results, respectively, must be BLOCK_UNRELEASED_CLAIM:R15, BLOCK_UNRELEASED_CLAIM:R14 and BLOCK_UNRELEASED_CLAIM:R9_R10. No case becomes a new canonical R-series record. The 20 baseline conclusions remain unchanged; the two former CAUSE tokens are now labeled TASK_SCOPE in the code and transcript. The guard checks the classroom release boundary; it does not authenticate a source or verify a real approval.

The original three release-guard controls remain required. The five additional controls in the complete program test a different boundary: the input schema and the distinction between fixed canonical metadata and separately labeled variants. An empty reference list cannot claim that records were supplied. The text "False" is not a Boolean false value; accepting it as a variant marker would admit invented impact into what a reader might mistake for the canonical packet. R15 does not supply an anchor, R14 does not supply complete coverage, and R1–R16 do not report a failed custodian contact. Those altered premises must not silently enter the canonical case.

custodian_available=True in the original baseline remains a model default, not a verified successful contact. The default produces no collection or hold receipt. Requiring a separately labeled variant for a failed-contact scenario does not authenticate the default; both real contact status and any real preservation result remain unmeasured.

A declared variant can still contain an invented owner_state_check=True or source-safeguard flag. These guards do not verify those assertions. They validate classroom structure and provenance boundaries, not source truth, actual harm, probability, severity or permission. TRI2 remains an original OpenCyber Academy method rather than a NIST classification policy. The renamed TASK_SCOPE labels concern authorization of the task, not a demonstrated causal mechanism or human actor.

Limitations and diagnostic negative tests

The program does not read any case files, query logs, test retention policies, authenticate a source, assess human identity, or verify a business state. Values such as owner_state_check=True, no_new_edits_verified=True and source_safeguard_confirmed=True are invented premises for independent variants, not real evidence collected by the script. It cannot calculate probabilities, incident severity, actual retention deadlines or appropriate live containment.

The result labels are educational and may be rejected or refined by an organization's actual decision policy. The ordering of checks is simplified: first validate release, then flag invented canonical events, then describe dimensions and limitations. Real cases may have multiple independent uncertainties not represented here.

Further negative control: Set request_downgrade=True while leaving training_scope_verified_complete=False. The expected flag is DOWNGRADE_NOT_JUSTIFIED. Do not remove the quiet-query or source-retention caveats merely because a respondent asks for lower priority. Restore the baseline after the exercise.

Required learner output: Reproduce the 20 baseline lines and five input-guard lines from the complete program, then test the three release-guard challenges separately. Classify each scenario's most consequential limitation, explain why preservation and containment urgency can move independently, and write two versioned notes—09:05 and 09:20—with the correct release boundaries, owner, next authorized request and review trigger.

Lab assessment, five points: one point for exact output; one for three separate confidence layers; one for separating reported from verified impact; one for preservation versus containment review; and one for rejecting unsupported de-escalation, attribution and future-source use. This is supplementary assessment and creates no numbered exercise ID.


2.2c Two-role practical activity — frozen priority review and independent reassessment

Purpose: Determine whether a reviewer can explain why a provisional priority changed, stayed open or narrowed using the actually released source cards, without the writer's retrospective coaching or invented production evidence.

Roles: Round A/B Triage Analyst prepares and freezes the two source-bounded decision packets. Independent Review Lead evaluates the packet using the permitted R-series table and produces a separate, frozen reassessment. A facilitator may observe but must not supply missing evidence or reveal later cards prematurely.

Preparation and sealed packet

The analyst creates TRI2-A-v1 with checkpoint 09:05, R1–R8 evidence pointers, reported versus inferred statements, potential harm, separate confidence layers, owner, a proposed authorized training-owner check and 09:15 review condition. The analyst then freezes that packet unchanged.

Only then does the facilitator release R9–R16 for the 09:20 reassessment. The analyst prepares TRI2-B-v1, identifying which statements changed because R9/R10/R11/R12/R14/R15 became available. The packet does not contain R17–R24 or later branch results. It preserves R15's unknown deletion anchor and R14's incomplete query coverage.

The analyst may add a separately marked synthetic variant card illustrating an approved addendum or custodian unavailability, but must give it a distinct name and must not mix its result into the canonical Round B note.

Independent review

Without asking the analyst to explain what they intended, the review lead:

  1. Reconstructs the evidence set legitimately available at each checkpoint.
  2. Classifies each major claim by record content, interpretation, and cause/attribution.
  3. Separates potential business harm from the consequences actually verified.
  4. Identifies preservation-urgency evidence and unknown loss clock.
  5. Identifies continuing-activity concern without treating R14 as a complete negative finding.
  6. Names the permitted next request, its custodian/owner and the relevant approval boundary.
  7. Identifies whether the 09:05 provisional note remained justified at the time and why 09:20 warrants a revised note.
  8. States the next review time or precise future observation that would trigger earlier reconsideration.

The lead writes TRI2-review-v1 and freezes it before the two roles discuss disagreements. A reviewer who uses R9 to fault the 09:05 analyst for ignoring it fails the chronological requirement. A reviewer who describes the notice as proof that logs were deleted also fails the source-semantics requirement.

Adjudication and improvement

Compare the two frozen notes and receiver answer with the canonical sources. Record whether a problem came from a missing source pointer, an unsupported interpretation, an inappropriate priority change, an incomplete owner/request field or a violation of the release boundary.

If the packet requires correction, create TRI2-A-v2 or TRI2-B-v2 with a documented change. Do not overwrite the old note or pretend that the revised understanding existed earlier.

Bounded pass: Both checkpoints have correct release sets, all three confidence layers are distinguished, reported harm is not counted as verified impact, preservation urgency is separate from containment concern, and the next proposed request and review trigger are authorized and specific.

Critical fail: Attribution is asserted without supporting evidence; R14's quiet result is generalized beyond its unknown query bounds; an exact R15 deletion deadline is invented; a synthetic variant is imported into canonical R1–R16; an earlier decision is justified with later sources; or a production control is selected/executed without the proper fictional approval path.

Limits: This practice tests two learners' written reasoning under a synthetic tabletop, not the actual capacity of an organization to contact a custodian or contain a cyberattack. A correct priority note does not prove the next request was performed.

Worked checkpoint

At the 09:05 checkpoint, classify only released observations and state a provisional priority. At 09:20, reopen the assessment with newly released records. The later conclusion does not rewrite the earlier evidence boundary.

Practice — Lesson 06

EX014 — Triage question

Objective: turn a detector label into testable questions. Inputs: R9–R16 and the Module 1 service map. Output: a two-row table with question, relevant supplied record, authorized owner or source to ask next, and an answer that would change triage. Begin with the label “suspicious booking edit” and avoid using the label as evidence. A score of 2 requires two different discriminating questions and a bounded source for each.

TRI2 refinement: In each of the same two rows, distinguish (a) the supplied record statement, (b) the interpretation the authorized check could strengthen or weaken, and (c) the causal or human-actor claim still unproved afterward. A B19 task-scope check and a booking-state comparison answer different questions. Do not add a third row or report a proposed check as completed.

Worked answer: EX014 in the final appendix.

EX015 — Hypothesis table

Objective: keep competing explanations live. Inputs: H1–H3 from section 2.1 and R1–R16. Output: three rows, each with a supporting record, a contradiction or limit, a narrow next authorized check and what result would weaken that hypothesis. You may state that a record is weak support. A score of 2 requires at least one check that could genuinely discriminate between explanations rather than simply restating the same log.

Discriminator check: In each row, the named check must be capable of producing the stated weakening result. Identify the correct producer and field: R9 reports a booking-audit room change, whereas R11 reports a time-display concern. A task-approval document and identity-session detail answer different questions. This is a requirement within the existing three-row artifact and 0–2 rubric.

Worked answer: EX015 in the final appendix.

EX016 — Priority without false certainty

Objective: set urgency without inventing a severity fact. Inputs: R9–R16 at the 09:20 discovery point. Output: a short decision note with separate fields for potential patron harm, retention or ongoing-activity urgency, confidence in the leading hypothesis, provisional priority, decision owner and an observable review trigger. A score of 2 requires a reason for the priority and a deadline or event, not merely a color or number.

Extension: Require one sentence explaining why retention urgency can be high while evidence of account compromise remains inconclusive. Identify what would cause the priority to increase and what would justify a less urgent review. Do not turn an unknown deadline into a guessed timestamp.

For full credit: The learner identifies R15's possible evidence-loss path, R9/R11's potential booking harm, unresolved attribution, the incident lead's decision authority and the 09:35 review or an earlier verified trigger.

Additional IM2 discriminator for EX016: At Round B, distinguish the R15 concern about detailed booking-audit records relevant to R9 from the retention of R11's help-desk report or R13's identity source, whose policies are not supplied. Separately assess potential booking harm, uncertain cause and the absence of a measured affected-patron total. The independent IM2 fixture supplies no Harbor Library outcome. Keep the same short decision note and 0–2 rubric.

TRI2 refinement: Keep the note short. State separately that the synthetic records are supplied, B19 task authorization is unresolved, and cause or human attribution is unproved. Split time sensitivity into source-preservation contact and a conditional human review of containment; R14's quiet summary has incomplete coverage and R15 has no confirmed deletion anchor. Name the authorized P-A or impact-check request, the incident lead, and the 09:35 or earlier evidence-triggered review. This adds precision to the same artifact and 0–2 rubric, not another exercise.

Worked answer: EX016 in the final appendix.

Lesson 07 — Preservation, timestamps and package integrity

Week 2, day 2. Work through the teaching and the worked checkpoint before attempting the assigned exercise(s). Compare your artifact with the final worked result only afterward.

2.3 Preserve what supports the next decision

NIST SP 800-86 provides guidance on incorporating forensic techniques into incident response, including work with different data sources. Its publication page expressly frames the guidance as an IT investigation aid and calls for management and legal consultation on applicable requirements. This rapid course therefore teaches a minimal reproducibility record, not a certified forensic collection method or a legal chain of custody.

For each synthetic record that matters, note the source system, the exact time range and timezone, the person who exported or copied it, the permission used, the collection time, where the original remains, where the working copy is stored and how access to it is limited. Record transformations: an export may sort rows, normalize timestamps or omit fields. Preserve the unmodified export when authorized, and analyze a separate working copy. If a source is volatile or automatically deleted soon, tell the incident lead so the collection decision can be prioritized.

An integrity hash can detect whether a particular byte sequence changes between two checks. For example, a team can calculate SHA-256 over a copied file at collection and again before review. A matching value supports that the checked copy has not changed between those checks, assuming the method and record are trustworthy. It does not prove the source log was complete, that its clock was correct, that the exporter had permission, or that the events actually happened. A hash of a screenshot says nothing about omitted rows. Explain these limits in the case note.

Lesson table; scroll horizontally to read all columns →

Collection-note field Example for a synthetic classroom export
Case and item HL-IR-001, item E1
Source and range Proposed booking-audit event-time selection, 08:30–09:15 UTC; producer, filter and available rows remain to be confirmed
Collector and authority Analyst; fictional incident lead's classroom authorization
Collected at Not performed in this illustrative card; R9–R16 reach the learner only at 09:20. Record the actual later local time if a teaching copy is created
Method Planned only: transcribe permitted synthetic rows as UTF-8 after release in an authorized scratch directory; this is not a production export
Copy and access Planned local training copy E1.txt in an authorized scratch directory; learner only if created
Integrity note SHA-256 of the exact saved bytes, recorded before and after analysis if a file is created
Transformation and limitation No production source; table is invented, and export formatting can change bytes

This table is a model for an exercise note. A real organization needs its approved preservation, retention, privacy and evidence-transfer procedures. Never collect broader data merely to make a training exercise look realistic.

Prioritize a preservation request before copying

NIST SP 800-61 Rev. 3, RS.AN-06 and RS.AN-07 addresses the provenance, integrity and restricted access of response records, and the collection and retention of incident data under an organization's procedures. The preservation queue below is OpenCyber Academy's classroom method for deciding what to request next; it is not a NIST form or a substitute for an organization's policy. The question is not “Which source contains the most data?” It is “Which authorized, minimal observation could change the next decision, and which opportunity to obtain it may be lost?”

Write a separate card for each candidate source. Name the decision it could change, the smallest relevant field and interval, its custodian, the known or unknown loss path, the approval needed, the expected handoff artifact and the claim that would remain unproved even after collection. This reuses Module 1's proposed evidence-readiness contract, but now the case has a live decision point. A source that may expire soon deserves immediate contact even if the cause is uncertain; a source with no confirmed loss clock may still need a parallel availability check. Do not convert the words “high priority” into an unverified deadline. Conversely, an indefinitely retained document is not automatically less important to the hypothesis: it may simply permit a later request after the fragile source is safeguarded.

For a copy request, distinguish four states: proposed, a bounded request has been written; approved, an accountable owner has authorized its stated scope; collected, a specified copy has actually been acquired with an acquisition receipt; and verified, a reviewer has performed and recorded specified checks on that item. State what was verified: bytes, selection, provenance or another named property, not “everything.” Protecting records in place does not itself move a copy request to collected. A source-hold request follows a separate control lifecycle, including reported application and a bounded effect check where such a mechanism exists. Neither lifecycle substitutes for the other. Record each actual state change, actor, time and source pointer; leave missing receipts pending. This is the book's teaching model, not a required NIST form. The relevant NIST principles concern recording investigative actions and preserving the integrity and provenance of incident information under organizational procedures; see SP 800-61 Rev. 3, RS.AN-06 and RS.AN-07.

When two requests compete for attention, compare them without inventing a numerical risk score:

Lesson table; scroll horizontally to read all columns →

Question What to record Failure of a shallow answer
Decision value Which current choice could change, and what observation would discriminate? “Useful someday” gives no reason to expose unrelated data now.
Loss and delay What policy, observed failure or owner statement supports the loss concern? Is the policy anchor known? Adding a stated retention period to an event timestamp may fabricate a deadline.
Authority and feasibility Who can approve, who can retrieve, which selection is feasible, and what if the custodian cannot act? An analyst's urgency does not grant permission or prove that a source is accessible.
Exposure and fidelity Which people and fields are included, what filters or transformations are applied, and how will an unmodified authorized copy be distinguished from a working view? A broad export increases exposure; a narrow, undocumented filter can hide relevant rows.

The first contact may be the incident lead to obtain approval, the source custodian to establish a retention anchor, or both through an approved escalation path. Several requests can proceed in parallel; the queue ranks decision and loss concerns, not a fixed order of technical commands. Keep the case record and the protected evidence store separate. A case note may point to an item and its access owner without copying sensitive raw records into a widely read ticket. Record the retention rule for the collected copy separately from the source's own deletion rule. If an expected source is unavailable, record who confirmed that, which interval or field is missing and how the next decision must be narrowed. Do not fill the gap with an invented export.

Event time, discovery time and collection time are different facts

An event may occur at one time, enter a logging service later and be seen by an analyst later still. In the supplied classroom table, R9 says that an edit was recorded at 09:03 UTC. R15, at 09:14, communicates a retention rule; it does not restart the retention period for R9. Without the actual source's deletion policy, no learner can calculate an exact expiry from those two timestamps. The safe operational question is: What is the oldest relevant record's retention deadline at its source, and how soon can an authorized custodian preserve a narrow copy? Escalate that uncertainty instead of writing a fabricated deadline.

When merging records from different systems, retain the original timestamp and its timezone or offset. Add a normalized UTC field separately and record how it was obtained. A timestamp without an offset is ambiguous; a clock that is five minutes slow may reverse apparent order. The case table supplies UTC for teaching, but a real analyst would check source clock configuration and collection delay before claiming a precise cross-system sequence. R13's summary omits device and location detail, so the absence of those fields is a source limitation, not a negative finding about the session.

Preservation should also be proportionate. For the next Harbor Library decision, a bounded booking-audit interval and the account's relevant sign-in summary may be useful. Exporting all patrons, all departments or unrelated accounts could increase privacy risk without improving the decision. A narrow export can still omit a relevant event; record the selection rule and keep an authorized unmodified original when available. Module 2 teaches the logic of a collection request and a copy-integrity check. Production evidence handling belongs to the organization's approved procedures and qualified personnel.

Source time, arrival, visibility, query, release and collection

A timestamp must identify both whose clock it uses and what transition it describes. A useful ledger distinguishes these fields:

Lesson table; scroll horizontally to read all columns →

Field Meaning and boundary
Source event or source-record time When the source reports an operation, document observation or notice; identify which kind rather than calling every card time an operation time
Receiver arrival time When a named collector or transport accepted a record, if actually measured
Query-visible time When the selected store/index could return the record; an upstream arrival receipt need not establish search visibility
Query run time and selection When the query ran, separately from its source, filters and selected event-time interval
Learner release or analyst discovery When the classroom permitted use of a card; in a real case, record receipt and recognition separately where they differ
Copy acquisition time When a particular export or local teaching copy was actually acquired or created, not when its represented event occurred

Source clocks can be wrong, and pipeline implementations differ. A field called ingested_at needs a documented producer meaning: it might represent receipt, storage acceptance or indexing. Do not silently substitute one for another. NIST SP 800-92 discusses log generation, transmission, storage and use; this particular ledger is the book's instructional method, not a NIST schema.

In the canonical Round B packet, R9 reports a 09:03 booking edit; R10 has a 09:04 plan-record timestamp, not a verified approval time; R15 has a 09:14 notice timestamp, not a retention start; R16 has a 09:16 owner-note timestamp, not an executed account change. All four become available to the learner at 09:20. Their actual receiver-arrival, indexing and acquisition times are not supplied. R14's printed 09:12 is the supplied card's time, not a complete query-execution manifest. Only the explicitly hypothetical T1 card assigns a 09:12 query run and its particular event window.

The difference from a source-card time to 09:20 is therefore a source-to-learner-release interval, not a measured transport or ingestion delay. Preserve the original text and offset, the normalized UTC value, its source-time kind and the release boundary. RFC 3339 documents date-time representations and offsets; an offset does not authenticate a clock or reconstruct a missing timestamp. R9 remains unavailable to the 09:05 R1–R8 decision despite its earlier event time.

2.4 Worked example: the second alert

The following records are also fictional and extend Module 1. They are the complete input for this chapter's exercises. No live system or external account is involved. Exercise timing: the Module 1 analyst made the 09:05 decision from the then-supplied R1–R8 snapshot. This chapter releases R9–R16 to the learner at the simulated 09:20 review. Several events in the new table have earlier timestamps; the time an event occurred and the time the analyst received its record are different facts.

Lesson table; scroll horizontally to read all columns →

ID UTC time Source Observation
R9 09:03 Booking audit desk-a changed booking B19 from Room 1 to Room 3.
R10 09:04 Training plan The planned exercise mentions B17 and B18; it does not mention B19.
R11 09:06 Help desk A second patron reported that the time on B19 looked wrong.
R12 09:08 Staff interview note The staff member said they edited B17 and B18 during training but did not recall editing B19.
R13 09:10 Identity summary The account had one session in the available time range; the summary omits device and location detail.
R14 09:12 Application audit No further booking edits are shown after R9 within the queried range.
R15 09:14 Retention notice Detailed booking audit records are deleted after 24 hours unless an authorized copy is retained.
R16 09:16 Service-owner note The owner requests an impact check before any account change and can approve a short booking-service pause if needed.

Step 1: distinguish the operation from the reported symptom. R9 is labeled booking audit and reports a change to B19's room, from Room 1 to Room 3. R11 is a help-desk report that B19's time looked wrong. The shared booking ID makes a bounded comparison useful; it does not make these the same field or establish that the room change caused the time complaint. R14 is separately labeled application audit. Confirm source identity and field semantics before treating R9 and R14 as one continuous log.

Step 2: split the hypotheses into testable propositions. H1 asks whether the relevant edits were within scheduled training. R10's plan names B17/B18, not B19; R12 is a recollection rather than an exhaustive approval record. H2 asks whether the B19 operation fell outside the approved task set. Neither proposition directly identifies the person controlling the account. Ask the training owner for the complete applicable plan, version, effective approval time and any B19 addendum. A document received at 09:20 may describe an earlier approval; its arrival time alone neither validates nor invalidates that earlier authority. A newly issued retrospective approval must not be presented as permission that existed at 09:03.

H3 addresses the patron's reported display or synchronization problem. Test the reported time separately from the audited room change. A proposed comparison should identify B19, the exact field, an owner-approved target and version, the authoritative stored value and its observation time, and the patron-facing value with its own observation time. None of these comparison values is supplied by R1–R16. The following are conditional interpretations, not new case facts:

Lesson table; scroll horizontally to read all columns →

Future comparison, if authorized and adequately evidenced Narrow conclusion Remaining limitation
The relevant patron view differs from the authoritative stored time, while that stored time agrees with the applicable approved target Supports a discrepancy between those observed representations Does not establish the synchronization mechanism or explain the room edit
Contemporaneous view and stored time agree with each other but both differ from the applicable target Weakens a display-only explanation for that observed state; supports a bounded target/state discrepancy Does not establish its cause or exclude a separate earlier transient display problem
A target, version, relevant observation time or trustworthy source is missing The proposed comparison remains unverified Neither “correct” nor “corrupted” is warranted from the complaint alone

A later correct view cannot, without historical evidence, prove that the earlier complaint was mistaken. Likewise, a room mismatch does not independently corroborate a time-field complaint. Keep the observations, hypothesized relationship and unanswered question separate.

Step 3: request the smallest discriminator that can change the decision. The training owner can address task authorization; the service owner and application custodian can define the bounded field comparison; the identity custodian can address a separately authorized session-correlation question. An identity-summary request cannot substitute for an approved-task-list check. Request source availability and preservation in parallel where justified by R15, without declaring an export, hold or loss. The incident lead retains the 09:35 review, or an earlier review on a separately verified new unexplained edit or substantiated source-availability concern. No account or service change is authorized merely by producing this analysis.

Lesson table; scroll horizontally to read all columns →

Case decision at 09:20 UTC Rationale Owner Review trigger
Preserve the supplied booking and identity records in the classroom case note; request an authorized impact check in a real response. The B19 edit is not yet explained and detailed audit retention is short. Incident lead is the proposed approval owner for bounded analysis and a separate source request; analyst records only the synthetic case note unless a local exercise copy is actually created and documented. P-A/P-B remain proposed. Training-owner answer, new booking edit or the next review at 09:35 UTC.

This decision does not clear or accuse the staff member. It states what is known, what can be lost and who may authorize the next action. If the impact check found broad damage, a containment proposal would need to reach the service owner and incident lead promptly; Module 3 will compare those options.

Worked 09:20 preservation queue

The learner receives R9–R16 at the simulated 09:20 review. R9 reports a 09:03 edit, but was not available for the learner's 09:05 decision. R15 reports a 24-hour policy concerning detailed booking-audit records, not a measured countdown or a completed deletion. The policy anchor, oldest-row availability, enforcement behavior and any authorized copy or source hold remain unconfirmed. Ask the custodian what the copy provision means in this source's actual procedure; do not assume that saving an export changes the source's removal mechanism. The queue records proposed requests, not approvals or completed preservation. R15's rule must not be borrowed for R14's separately labeled application-audit source, R11's help-desk record or R13's identity source without an explicit custodial mapping and policy confirmation.

Lesson table; scroll horizontally to read all columns →

Request and decision value Loss/coverage question Owner and proposed minimal scope State at 09:20 and remaining limit
P-A: detailed booking audit for B17–B19, plus R14's application-audit query description; could reconstruct recorded edits and support a separate impact comparison. R15 names 24-hour deletion, but the policy anchor, oldest available row and exact last safe time are unknown. R14's filter and ingestion coverage are also unspecified. Incident lead seeks approval; application custodian checks a proposed 08:30–09:20 UTC event window, booking IDs and available raw fields, then records each source, filter, run time and export method. Proposed. No detailed copy, confirmed deadline, complete query or approved booking state has been supplied.
P-B: identity-session detail relevant to desk-a; could test whether an application action shares a session key. R13 is only a summary without device or location; its source retention and key semantics are unknown. Incident lead asks identity administrator for availability, retention and a restricted time/field selection. Any cross-source join requires schema and clock checks. Proposed. No person, device, location or actor attribution follows from R13.
P-C: approved training task list and version; could test whether B19 was actually in scope. R10 lists B17/B18; the plan's version, approval time and completeness need confirmation. No deletion deadline is supplied. Training owner supplies only the relevant signed or approved task list and its version history through the approved case route. Proposed. R12's recollection is context, not a substitute for the plan or proof of who edited B19.
P-D: help-desk report and service comparison; could bound user-facing harm. R11 is a report, not a measured patron count; the database and displayed view may disagree. Service owner and help desk plan a restricted B19 comparison, separating the report from a patron-facing check and an approved state. Proposed. No population-wide impact estimate or restored-service claim is supported.

P-A receives prompt attention because the fictional retention notice identifies a plausible loss path and the detailed rows could change the next containment or impact decision. The lead should seek a custodian-confirmed policy anchor and a safe collection time immediately. R9 is labeled booking audit while R14 is labeled application audit; the request must ask whether these are the same producer or two different paths, rather than treating their summaries as one complete log. P-B can run in parallel because its retention is unknown, not known to be unlimited. P-C and P-D remain important to interpretation and harm; they are not discarded merely because the audit may be more volatile. The proposed P-A range is deliberately broader than R9's single timestamp yet bounded by the booking question. It is a request for custodian review, not an assertion that those filters exist, that the source contains every event, or that the lead has approved exporting patron data.

Suppose the application custodian later returns a restricted file. The analyst would record its actual collection time, the approved selection, source timezone, any transformation, byte count or applicable integrity check, storage/access owner and reviewer. The analyst would also ask for the query manifest behind R14's quiet summary: source, booking/account filters, half-open event interval, query run time, ingestion coverage and source-health limitations. Without that manifest, a copied R14 sentence still cannot establish that no edits occurred after R9. If the custodian instead reports that the detailed interval has already expired, the response lead records the failed request and who confirmed it, preserves the remaining source pointers within scope, and changes the claim from “we checked the detailed audit” to “the detailed audit was unavailable for this interval.” Neither branch may be silently backdated to 09:05.

A concise 09:20 handoff can therefore say: “R9 records a B19 edit at 09:03 UTC; R10 omits B19 from the listed training tasks, and R15 reports a 24-hour detailed-audit retention rule whose anchor we have not verified. At 09:20 we propose P-A to the incident lead and ask the application custodian to confirm availability, selection and the actual policy deadline; no production copy, hold or account restriction is recorded here.” This wording lets the next analyst see both the reason for urgency and the boundary of current evidence. Exercise EX018 asks the learner to make that request independently.

Preservation as an authorized transaction

A useful preservation request names why a source matters before specifying how much data should be copied. The next question is whether B19 was an approved operation and whether the booking service's current state matches what the service owner expects. This favors bounded booking-change evidence, an approved task-list version and appropriately limited identity details—not a bulk export of all patron records.

For each source request, prepare:

  1. Decision purpose: what the evidence could change.
  2. Producer and fields: what system is expected to know, including timestamp and identifier semantics.
  3. Selection: the smallest defensible time and object range, including explicit exclusions.
  4. Custodian and authority: who can retrieve the data and who can approve the operation.
  5. Retention question: whether the source still contains the interval and when it may cease to.
  6. Receipt design: which exact source, query, object, acquisition time and transformation would be recorded if collection happens.
  7. Residual uncertainty: which causal or completeness claims would remain unproved.

The existing P-A and P-B queue illustrates this. P-A can be addressed promptly because R15 reports a deletion risk for detailed booking audits. P-B can proceed in parallel as an availability inquiry because identity-source retention is unspecified. Neither source should be treated as collected merely because the analyst assigned it priority.

The states proposed → approved → collected → verified are not interchangeable. A written request is proposed. An incident-lead authorization covers only its recorded scope. A collected object needs an identifiable receipt. Verification identifies what was checked, such as the exact copied bytes or selection manifest, and what was not checked. Privacy restrictions and access controls apply to the collected copy independently of the source's original retention policy.

NIST SP 800-86 discusses forensic work across data sources from an IT incident-response perspective and expressly does not offer legal advice or an all-inclusive procedure. Harbor Library's request card is a beginner teaching method, not a chain-of-custody certification or permission to collect real personal data.

2.5 Contained lab: preserve and check a synthetic extract

The lab below is deliberately smaller than a forensic acquisition. It teaches which bytes a digest covers and which conclusions still require other evidence. Use a new, empty, authorized scratch directory on a computer you control, with Python 3 and no network connection. The generated text is invented for this book; it contains no real account, endpoint or patron data. Running the script creates two small files in the current directory and refuses to overwrite existing names. If either name already exists, stop and choose a different scratch directory; do not remove an existing file to make the lab run.

The extract has two rows that transcribe R9 and R14. It is a learning fixture assembled from this chapter's table, not an original log from Harbor Library. R14 summarizes the queried audit range; it does not assert that no later edit occurred outside that range. Save and run the following as fixture.py inside the scratch directory:

from hashlib import sha256
from pathlib import Path

payload = (
    b"case_id|record_id|time_utc|source|observation\n"
    b"HL-IR-001|R9|2026-10-11T09:03:00Z|booking-audit|desk-a changed B19 from Room 1 to Room 3\n"
    b"HL-IR-001|R14|2026-10-11T09:12:00Z|application-audit|no further edits in the queried range after R9\n"
)
with Path("E1.txt").open("xb") as output:
    output.write(payload)
saved = Path("E1.txt").read_bytes()
print(len(saved), sha256(saved).hexdigest())

altered = saved.replace(b"Room 3", b"Room 4")
with Path("E1-modified.txt").open("xb") as output:
    output.write(altered)
saved_altered = Path("E1-modified.txt").read_bytes()
print(len(saved_altered), sha256(saved_altered).hexdigest())

Failure-path check: two exclusive creations are not one atomic transaction. The required new empty directory avoids ordinary filename collisions. Python's exclusive-create file mode refuses to overwrite an existing target, but each call protects only its own file. In a separately labeled failure rehearsal where only E1-modified.txt already exists, the first write can create E1 and print its digest before the second write raises FileExistsError. That is a partial local attempt, not completion of both files and not an overwrite of the pre-existing file. Record which file was actually created, which operation failed and what was left unchanged. Stop; do not delete the existing object or switch to an overwrite mode to obtain the expected transcript. A new authorized empty directory permits an independent attempt with its own recorded state.

For a collection note, record the actual local time when the creation is performed. A recorded start/end interval may honestly bound the operation; a later observation time or filesystem timestamp with unverified semantics must not be relabeled as an exact creation receipt. When a time was not recorded, say so. The synthetic 09:20 review does not supply a local acquisition time.

Procedure. First, predict whether both files will have the same byte length and whether their SHA-256 digests will match. Then run the script and record both output lines. Read E1.txt and E1-modified.txt to locate the intentional one-byte change. Re-run the first digest on E1.txt and compare it with the saved value. Finally write a collection note identifying the exact file, byte count, local collection time, synthetic source, authorized classroom scope, transformation and storage location. If you cannot run Python, reason from the displayed bytes and use the worked result to check your conclusion; software execution is not required to answer the conceptual question.

The byte count is a useful reproducibility check, but equal length is not equal content. A matching digest on E1.txt at two checks supports the limited statement that the bytes checked were the same at those two times, assuming the digest calculation and recorded values are trustworthy. A differing digest for the deliberately altered copy shows that this check detects that particular edit. Neither result verifies the truth of R9 or R14, completeness of the original query, correctness of a clock, identity of the editor, legal authority or whether data left the service. The example uses SHA-256 as an integrity check, not as proof of authenticity.

Expected submission: the two byte counts and digests, one sentence explaining why the hashes differ, a collection note for E1.txt, and two explicit limits on the integrity claim. Rubric: one point each for correct byte/digest comparison, correctly identified modification, complete source-and-authority note, and limits that distinguish copy integrity from source truth. A score of 4/4 means the classroom task is reproducible, not that a real forensic process has been completed. The exact expected output and a model note are in the final worked-results section after EX026.

A digest needs an object, a check time and a bounded claim

The E1 exercise has two different objects before its second file is read: altered, a byte string in the Python process, and E1-modified.txt, a named file. Hashing the first describes the intended output buffer. Reading the second and hashing the returned bytes describes what that read obtained from the named file. Those claims can agree in an ordinary successful run, but they are not the same measurement.

The revised listing above uses saved_altered = Path("E1-modified.txt").read_bytes() after the second file is closed and computes its second printed result from saved_altered. The first file already used this read-back pattern. The payload, intentional one-byte change and two expected output lines are unchanged. Python's Path.read_bytes documentation describes reading a file's binary contents; exclusive creation addresses a different question: whether an existing filename is overwritten.

Neither operation authenticates the original producer, verifies a storage device's crash durability, locks out a concurrent writer or records an uninterrupted custody history. Use the authorized private scratch environment specified above, rather than a shared or adversarial directory. A read-back error is a failed measurement, not a reason to substitute the intended buffer and label it a successful file check. Record the actual partial state and error; leave unperformed checks unperformed.

A reproducible integrity statement therefore needs at least the named object, the bytes or digest actually measured, the check time or recorded operation interval, and the comparison reference. The reference must be preserved as a separate record. Computing a new digest and silently replacing the old reference does not explain an unexpected difference. A timestamp in the file describes its content; it is not the time at which the analyst created or checked that file.

Intended byte string --write--> Named local file --read--> Bytes obtained
       |                                                     |
       v                                                     v
Hash of intended output                              Hash of this read
                                                             |
                                             Compare with retained reference
                                                             |
                                             Report object, time and limits

Text alternative: A buffer hash measures the buffer. A file read produces the bytes for a file-check hash. Compare that measured value with the retained reference and identify the check's time and object. None of these steps establishes source truth, full query coverage, uninterrupted custody or production collection authority.

Equal endpoints do not reconstruct the interval between them

Suppose an authorized reviewer checks a copy at two instants and obtains the same digest. That is useful evidence about those observations. It cannot establish that the copy remained unchanged at every unobserved instant between them. The distinction does not require a hash collision: a byte sequence could change and later be replaced by exactly the original sequence.

The following independent example supplies three invented in-memory states, named first, middle and last. They are not observations of E1, an R-series record or a real storage system. The middle state is supplied solely to make two possible histories distinguishable. Run this complete Python 3 listing in a fresh session. It creates and changes no files and needs no network access.

from hashlib import sha256

# Independent in-memory illustration, not E1 or an R-series record.
original = b"practice-value=3\n"
changed = b"practice-value=4\n"

histories = {
    "UNCHANGED": (original, original, original),
    "CHANGED_THEN_SAME": (original, changed, original),
    "CHANGED_AT_END": (original, changed, changed),
}
for label, states in histories.items():
    first, middle, last = (sha256(value).hexdigest() for value in states)
    print(f"{label} endpoints_equal={first == last} "
          f"middle_equals_first={middle == first}")

Work the example before running: UNCHANGED and CHANGED_THEN_SAME have exactly the same first and last bytes. An observer given only those endpoints cannot distinguish their histories. The supplied middle observation separates them. CHANGED_AT_END instead leaves different bytes at the final observation, so the endpoint comparison detects that particular change. The program illustrates limited observability; it does not determine who changed a file, whether any real change occurred, or how a production evidence store should be implemented.

The appropriate wording is “the measured values matched at the two recorded checks”, not “the copy could never have changed.” Additional authorized custody records, access controls and observations may strengthen a real preservation account, but their existence and scope must be evidenced separately. A third matching sample would still not observe every instant. NIST SP 800-86 provides source-aware forensic guidance in an IT response context, not a certification that this classroom copy has legal evidentiary standing.

Independent checkpoint practice

First predict and then reproduce the program's three lines. Submit three claim-review rows with claim | supported finding | missing evidence | next safe step for these deliberately faulty statements: A, “the former sha256(altered) expression was a read-back of the second file”; B, “matching endpoint hashes prove the copy never changed”; and C, “the second exclusive creation failed, so the first file was never created.” For C, use the explicitly supplied failure scenario in which only E1-modified.txt existed initially and the first output line was printed. Do not infer that scenario from every possible error.

Then predict the E1 program's outcome for four separately prepared starting states: neither output name exists; only E1 exists; only E1-modified exists; both exist. Record expected printed-line count, whether either new file is created and which existing bytes must remain untouched. These are predictions unless actually tested. The ordinary learner run still requires a fresh authorized directory with neither output present; do not delete an existing object to create a convenient starting state.

Use EX019's existing collection-note format to record only what was actually created and measured. Keep EX020's five claim rows, adding the endpoint limitation to their existing explanations rather than treating it as a new exercise. This supplementary checkpoint does not change the course's exercise count or confer collection authority. Assess the artifact by whether the learner identifies the measured object, preserves the initial and partial states, limits the endpoint inference and proposes a source-supported next check. Worked checkpoint answer.

2.6 Temporal coverage lab: what the analyst knew, and when

A useful timeline has at least two clocks: event time, as recorded by a source, and discovery time, when the responder received or recognized that record. A third time, collection time, marks when a copy was created and should be recorded separately. A query also has a coverage interval: the source and filters searched, the beginning and end of the search, and any ingestion delay or missing fields. These fields answer different questions. Sorting on one timestamp without naming which clock it represents can produce a fluent but false case history.

The Harbor Library example makes this visible. The learner's 09:05 decision used R1–R8. R9 records a 09:03 edit but is released to the learner with R9–R16 at the 09:20 review. The later discovery does not move the edit forward in time, and the earlier event time does not make it legitimate evidence for the 09:05 decision. An analyst should preserve both times so a reviewer can reconstruct what was actually knowable at each checkpoint. This is an original classroom workflow consistent with the source-aware investigation described in NIST SP 800-86; that older guide does not prescribe this book's specific fixture or scoring rule.

Exercise-only card T1 — illustrative query metadata. Imagine that a custodian now supplies the missing scope for R14: source application-audit; booking-edit events with event times at or after 09:00 and before 09:12 UTC; account filter desk-a; query run at 09:12 UTC. T1 is not one of R1–R16 and must not be used to rewrite the 09:05 decision or to pretend the original R14 supplied its own start and end. Even with T1, the statement “no further edit after R9 in this query” covers only that source, account filter and interval. It says nothing about another account, a delayed record or an edit at 09:13. Without T1, R14's exact interval remains unknown.

The following optional lab runs entirely in memory with Python 3's standard library. It creates no files, sends no network requests and uses only fictional timestamps. R10's 05:04-04:00 text is an exercise rendering of its existing 09:04 UTC value, not a new event or a claim about any real location's timezone rules. The offset is explicit; the script does not guess one.

from datetime import datetime, timedelta, timezone

rows = [
    ("R9", "2026-10-11T09:03:00+00:00", "2026-10-11T09:20:00+00:00"),
    ("R10", "2026-10-11T05:04:00-04:00", "2026-10-11T09:20:00+00:00"),
    ("R15", "2026-10-11T09:14:00+00:00", "2026-10-11T09:20:00+00:00"),
]

def utc(text):
    try:
        value = datetime.fromisoformat(text)
    except (TypeError, ValueError) as exc:
        raise ValueError("INVALID_TIMESTAMP") from exc
    if value.tzinfo is None or value.utcoffset() is None:
        raise ValueError("OFFSET_REQUIRED")
    return value.astimezone(timezone.utc)

def release_interval(source_text, release_text):
    source, released = utc(source_text), utc(release_text)
    if source > released:
        raise ValueError("SOURCE_AFTER_RELEASE")
    if any(value.second or value.microsecond for value in (source, released)):
        raise ValueError("MINUTE_PRECISION_REQUIRED")
    minutes = (released - source) // timedelta(minutes=1)
    return source, released, minutes

for record_id, source_text, release_text in rows:
    source, released, minutes = release_interval(source_text, release_text)
    print(
        f"{record_id} source={source:%H:%M} UTC "
        f"released={released:%H:%M} UTC interval={minutes} min"
    )

# Independent malformed-input controls, not additional R-series facts.
for label, text in (
    ("NO_OFFSET", "2026-10-11T09:03:00"),
    ("FUTURE_SOURCE", "2026-10-11T09:21:00+00:00"),
    ("BAD_CLOCK", "2026-10-11T25:03:00+00:00"),
    ("SUB_MINUTE", "2026-10-11T09:03:30+00:00"),
):
    try:
        release_interval(text, rows[0][2])
    except ValueError as exc:
        print(f"{label}: {exc}")
    else:
        raise AssertionError(label + " was not rejected")

Time-label clarification and input contract: source means the source-card time: an edit for R9, a plan record for R10, and a retention notice for R15. released means the learner's 09:20 release. The dates and offsets are the existing synthetic fixture, not new case evidence. This minute-aligned exercise requires an explicit UTC offset and refuses a source timestamp later than its release under the assumed comparable clocks. It rejects sub-minute values rather than silently rounding them. This is not a general RFC 3339 validator or a real clock-verification tool. In particular, Python's datetime documentation explains that converting a naive datetime can use the machine's local timezone; this program rejects that ambiguity before conversion.

Interpret the time field before ordering the records

A timestamp's representation and its meaning are different. Converting 05:04-04:00 to 09:04 UTC preserves the represented instant; it cannot tell us whether that instant is an operation, a plan record, an approval, a source query or an analyst's later acquisition. Sorting correctly formatted values without identifying those meanings can create a false causal or authorization sequence.

For example, a faulty reading says: “R9's edit is at 09:03 and R10's plan is at 09:04, therefore the operation preceded its authorization.” The ordering of the two supplied record times is correct, but the conclusion needs an effective authorization time that R10 does not provide. The plan-record time is not proof of when the plan was first written or approved. Ask the training owner for the applicable version and approval evidence; do not infer that authorization was either present or absent from this ordering alone.

The same error can create a fabricated preservation deadline. R15's 09:14 timestamp belongs to a retention notice. It is not a supplied retention-start timestamp for R9. Neither adding 24 hours to 09:14 nor adding it to R9's 09:03 establishes a deletion instant without the source policy's actual anchor and enforcement semantics. Keep the loss concern visible while the custodian clarifies it.

For EX021, retain five rows and annotate each supplied timestamp with its kind: R9 reports an edit; R10 identifies a plan record; R12 an interview note; R15 a retention notice; R16 a service-owner note. A note's timestamp is not automatically the time of every action discussed in it. Add a specific uncertainty in each row, not one general disclaimer that hides which inference is missing. The common 09:20 learner release remains a separate field or table note; it is neither a source event nor evidence that a local copy was created.

Independent self-check: Before opening EX021's final answer, explain why a plan-record time cannot settle prior authorization and why a notice time cannot start an unprovided deletion clock. Then identify the owner and missing record that could resolve each question. Correct arithmetic, correct source-time meaning and sufficient evidence for a decision are three separate requirements.

Predict the original three intervals and all four rejection reasons before running. None of the malformed-input controls is an R-series event. A chronological rejection calls for investigation of timestamp meaning and clock assumptions; it does not establish malicious activity. Keep ingestion, query execution and copy creation unknown when they have no separate evidence.

Procedure: (1) Before running it, convert R10's offset time by hand and predict the three source-to-learner-release intervals. (2) Run the code in an authorized local scratch environment, or calculate the outputs from the shown values if Python is unavailable. (3) Make a three-row table with record ID, original timestamp including offset, normalized UTC source-card time and kind, learner-release time, source-to-learner-release interval and whether the record was available at 09:05. (4) Draw the coverage interval from T1 as 09:00 ≤ event < 09:12 UTC and put R9, the T1 end and the 09:20 review on it. (5) Write two sentences: the strongest conclusion supported by R14 with T1, and the next authorized check needed to assess activity after 09:12. Keep the R15 retention notice separate from any assumed deletion instant.

Expected evidence: R9 is 09:03 UTC and released to the learner 17 minutes later; R10 is 09:04 UTC and released 16 minutes later; R15 is 09:14 UTC and released six minutes later. None was in the learner's R1–R8 snapshot at 09:05. With T1, R14 supports only that this narrow query showed no further matching edit before 09:12. The unqueried period after 09:12 is an evidence gap, not a verified quiet period. The learner should request a new bounded query or an approved source comparison, record its filter and end time, and check whether logging and ingestion were working. R15 reports a 24-hour detailed booking-audit policy with an authorized-copy provision; it does not give enough deletion-policy detail here to calculate the precise expiry of R9 from its event timestamp alone.

Assessment rubric: one point each for correct R10 normalization, three correct source-to-learner-release intervals, the 09:05 availability boundary, a T1 coverage statement that excludes post-09:12 activity, and a narrow authorized follow-up with retention uncertainty. Five points show the classroom reasoning is reproducible; they do not certify a real evidence acquisition or establish who edited B19. The printed output and a model coverage statement appear after the Module 2 synthetic-extract result in the final appendix.

A query envelope is narrower than a case conclusion

A log search is a measurement operation over a specified source and filter, not a universal statement about everything that happened. An empty or apparently quiet result is meaningful only in the context of its query envelope:

Lesson table; scroll horizontally to read all columns →

Envelope field What a reviewer must know
Producer/source Was the booking audit, application audit or another source queried?
Event-time interval What beginning and end were selected, and was the end exclusive?
Identity/booking filters Which account, booking IDs or operation kinds were included or excluded?
Query run time When did the result reflect the available index?
Ingestion completeness Through which event-time boundary was the relevant source demonstrably indexed?
Source health and retention Were generation, collection and storage functioning during the interval?
Returned rows and transformations Were rows missing, deduplicated, normalized or otherwise transformed?

R14 is intentionally narrow: it reports no further booking edits after R9 within its queried range. Canonical R14 does not provide the complete envelope. R9 is labeled booking audit while R14 is labeled application audit. Treating them as a single fully covering source would silently merge producers without evidence.

This distinction exposes two different kinds of "nothing found." One is no row matched this particular query at this query time. The other, much stronger statement would be the necessary producers were functioning, the selection was correct, the source's relevant events were all collected and indexed, and no qualifying event occurred in the bounded interval. The former does not establish the latter.

The existing T1 exercise supplies illustrative metadata only when expressly invoked, and T2 demonstrates late arrival. The new VQ2 lab below goes a step further: it asks when the metadata itself would be adequate to support even a bounded negative statement. A query that covers the correct event interval but was run before ingestion had caught up is insufficient; so is a query with complete ingestion for the wrong account.

NIST SP 800-92 provides primary guidance on log generation, collection and management. This older publication supports the need to understand those stages; the envelope and acceptance gate are original instructional models, not a NIST query specification.

2.7 Delayed log arrival: when a quiet query changes

Section 2.6 established a query envelope: source, event-time interval, account filter and time at which a query was run. A fifth field matters when records travel through a collection pipeline: ingestion time, when a record becomes available to the queried store. An event may have occurred inside the requested interval yet be absent from a query run before its arrival. The absence is a statement about the store's visible contents at that query time, not a final statement about every event that happened in the interval. NIST SP 800-92 treats log management as a process of generating, transmitting, storing and using records. It is a 2006 high-level guide, not a specification for the exercise algorithm below. This book's delayed-arrival fixture and scoring rule are original classroom material.

Use four different times or bounds in the case note. The event time comes from the producing application and may itself be inaccurate if the source clock is wrong. The ingestion time is when the queried store accepted the row; it says nothing by itself about when the analyst read it. The query run time is when the analyst or custodian asked the store for matching rows. The event-time window describes which event timestamps the query selects. A discovery time records when the result reached the response team. If the store can supply only some of these fields, mark the rest unknown. A later query returning more rows does not automatically mean a new event happened after the earlier query.

The canonical Harbor Library R14 card says only that its available query showed no further booking edits after R9 within its queried range. T1 in section 2.6 adds an exercise-only event window and account filter. It does not supply an ingestion-completeness guarantee. At a real response desk, an analyst might request source health, pipeline delay and collector error information from the logging owner, but this beginner exercise supplies no such operating measurements. An empty interval should therefore be written as “no matching row visible in this query as run,” followed by its bounds. It should not be written as “no edit occurred.”

Optional T2 replay: a late row in the same event window

T2 is a separate counterfactual replay, not a new R-series record and not an amendment to the main case. Reset to the fictional 09:12 query snapshot, then imagine the following four synthetic rows. L1 transcribes R9 for this replay. L2–L4 are invented only to exercise query semantics; do not use them in EX014–EX026, Module 3 or the Module 4 tabletop unless a question explicitly names T2.

Lesson table; scroll horizontally to read all columns →

Replay row Account Event UTC Ingested UTC Classroom observation
L1 desk-a 09:03 09:04 B19 booking edit, representing R9 in this replay
L2 desk-a 09:10 09:18 B21 booking edit, visible only after delayed ingestion
L3 helper-b 09:11 09:11 B22 booking edit under a different account
L4 desk-a 09:12 09:13 B23 booking edit at the excluded end boundary

The replay query selects application-audit booking edits under desk-a with 09:00 ≤ event time < 09:12 UTC. Its second condition is that the row was ingested no later than the query run time. The end boundary is exclusive. Assume all four clocks are correct, all rows belong to the same fictional date and no other filter exists; these are classroom assumptions, not evidence about production logging. First run the mental query at 09:12, then repeat the same event window and account filter at 09:20. Before looking at code, predict exactly which L IDs each run returns and which exclusion rule removes each other row.

This optional program operates only on the in-memory T2 fixture. Its last column is treated as query-visible time; T2 assumes no additional delay between the listed ingestion and search visibility. This simplifying assumption must not be applied to a real collector without checking its semantics. All rows use one fictional UTC day and comparable accurate clocks. The parser validates HH:MM, and the program rejects a row visible before its event under those assumptions. It validates the whole supplied packet before filtering, so an irrelevant account cannot hide malformed timestamps. It is still not a production parser, a transport simulation or an ingestion-completeness check.

import re

rows = (
    ("L1", "desk-a", "09:03", "09:04"),
    ("L2", "desk-a", "09:10", "09:18"),
    ("L3", "helper-b", "09:11", "09:11"),
    ("L4", "desk-a", "09:12", "09:13"),
)

def minute(text):
    if not isinstance(text, str) or not re.fullmatch(r"[0-9]{2}:[0-9]{2}", text):
        raise ValueError("BLOCK_INVALID_HHMM")
    hour, minute_part = map(int, text.split(":"))
    if not (0 <= hour <= 23 and 0 <= minute_part <= 59):
        raise ValueError("BLOCK_INVALID_HHMM")
    return hour * 60 + minute_part

def visible_at(query_time, sample_rows=rows):
    query = minute(query_time)
    validated = []
    # Validate the supplied packet before applying account/time filters.
    for row_id, account, event_time, visible_time in sample_rows:
        event, visible = minute(event_time), minute(visible_time)
        if visible < event:
            raise ValueError("BLOCK_VISIBILITY_BEFORE_EVENT:" + row_id)
        validated.append((row_id, account, event, visible))
    return [
        row_id for row_id, account, event, visible in validated
        if account == "desk-a"
        and minute("09:00") <= event < minute("09:12")
        and visible <= query
    ]

for query_time in ("09:12", "09:20"):
    print(query_time, ",".join(visible_at(query_time)))

for label, query, sample in (
    ("BAD_MINUTE", "09:12", rows + (("X", "desk-a", "08:63", "09:04"),)),
    ("INVALID_QUERY", "25:99", rows),
    ("VISIBLE_BEFORE_EVENT", "09:12",
     rows + (("X", "desk-a", "09:10", "09:00"),)),
):
    try:
        visible_at(query, sample)
    except ValueError as exc:
        print(f"{label}: {exc}")
    else:
        raise AssertionError(label + " was not rejected")

without_l2 = tuple(row for row in rows if row[0] != "L2")
print("OMITTED_L2:", ",".join(visible_at("09:20", without_l2)))
assert rows[1] == ("L2", "desk-a", "09:10", "09:18")

Predict the original two query results, the three malformed-input rejections and the omitted-row control. 08:63 must not be normalized accidentally to 09:03; 25:99 is not a valid query clock. An earlier visibility time contradicts this fixture's declared clock contract. In real evidence, such a discrepancy could instead reflect a misunderstood field or source clock and would need investigation.

The OMITTED_L2 case is equally important: every remaining field is well formed, but the later query returns only L1 because L2 was removed from that variant's input. Syntax validation cannot discover a record it was never given. Retain the complete original packet separately. Neither a successful parser run nor a repeated quiet query establishes source completeness. These controls remain outside canonical R1–R16 and add no exercise ID. Assess their reasoning within the existing five-point T2 rubric's timing, exclusion and bounded-follow-up criteria.

Work the result in three passes. First apply only the account and event-time filters: L1 and L2 qualify, L3 fails the account filter, and L4 sits exactly at the exclusive end. Then apply the ingestion cutoff: at 09:12 L2 is not yet visible, whereas at 09:20 it is. Finally compare the two results without changing the event times. The extra row in the second run is a late arrival, not a new post-09:12 event. Its presence would cause the response lead to revisit any conclusion that relied on the first query's apparent quiet period. It would not, by itself, identify who performed the edit or prove malicious intent.

The replay makes a useful operational distinction. A complete interval would require credible evidence that the source generated expected events, the transport accepted them, any buffering and retries were accounted for, the queried store indexed them, and the query covered the relevant source and filters through a defensible cutoff. An analyst cannot establish all that from the latest row's timestamp. A logging owner might supply a documented ingestion-lag bound or an independently monitored watermark, but the owner would have to state what that watermark means and what failures it excludes. This book supplies neither a watermark nor a service-level promise. A second empty query would remain bounded by its own run time and pipeline state.

Submission and rubric. Submit a two-row query time | returned IDs | reason for every excluded ID table, a one-sentence correction of the overclaim “nothing happened after R9,” and a request to the fictional application/logging custodian naming the event window, account filter, query run time and ingestion-health question. Score one point each for the 09:12 result, 09:20 result, correct explanations of L3 and L4, recognition that L2's event time did not move, and an authorized follow-up that distinguishes query coverage from account attribution. A 5/5 answer is a sound classroom interpretation; it is not verification of a real logging pipeline. The exact output and a worked answer appear in the final appendix after the Module 2 temporal-coverage result.

2.8 Evidence package audit: integrity is one link in a longer chain

The E1 file in section 2.5 is a synthetic transcription of book records R9 and R14. Its SHA-256 value can help a second learner check whether the working bytes changed. To assess a claim about the underlying booking service, a reviewer would still need to know who supplied the source, what query or export produced it, which fields or rows were excluded, what transformations were made, whether the source clock and logging path were functioning, and whether collection was permitted. NIST SP 800-86 discusses evidence work across data sources but explicitly does not offer an all-inclusive forensic or legal procedure. The exercise below is a training review of a small invented package, not a chain-of-custody certification.

Think of three layers. The source layer is an application or identity system and its original records. The copy layer is a specific exported byte sequence plus a query or selection manifest. The analysis layer is the learner's chronology, hypothesis table and decision note derived from the copy. A digest for the copy can check the copy's bytes at two times. It cannot retroactively create a missing source layer or prove the analysis layer's interpretation. Conversely, a well-written analysis cannot repair a silently truncated export. Give each layer a pointer in the case record so the next reviewer knows what can actually be reopened.

For the E1 classroom file, a minimal package manifest might contain the following fields. Status matters: a planned export and a collected copy are different artifacts. The example assumes the learner actually ran fixture.py; if they did not, they must label byte count and digest as expected values from the book, not measurements they performed.

Lesson table; scroll horizontally to read all columns →

Manifest field Model entry after the local fixture runs Limit the reviewer must retain
Item and status HL-IR-001/E1, learner-created teaching copy Not a production application export
Source and selection Book section 2.5 payload transcribing R9 and R14; two data rows R14's original query bounds remain unspecified without optional T1
Authority and operator Learner's local scratch directory under classroom permission No permission to collect a real library's records follows
Creation and transfer times Learner's actual local creation time; later handoff time only if a handoff occurs Do not substitute fictional event time for file creation time
Bytes and integrity check 235 bytes; SHA-256 of E1.txt as printed by the fixture A matching value checks these bytes, not source truth
Transformations UTF-8 teaching text assembled by the listed Python payload The payload omits source metadata not shown in R9/R14
Location and access Local named file, restricted to the learner or authorized reviewer The book provides no real storage-control evidence
Supported use Practice a bounded collection note and an integrity comparison No attribution, exfiltration or full audit-coverage conclusion

The second learner should check the manifest against the actual file and script, not against a confident sentence in a handoff. Confirm the byte count and digest for the file received, read the two data rows, inspect the source and selection note, then compare the proposed conclusion with the package's stated limit. If the values match but the query selection is unknown, the second learner may say the copy is byte-consistent with the recorded digest; they may not say the queried service had no other edits. If the values differ, retain both copies and their labels in the classroom exercise, stop relying on the disputed copy, and ask which bytes were changed or transferred incorrectly. Never silently overwrite one copy with the other to make the hashes agree.

Optional peer-audit drill. Imagine three variants of a package handed to you at the 09:20 review. Variant A contains the expected E1.txt, its 235-byte count, digest, script and the manifest above. Variant B contains E1-modified.txt but labels it E1.txt while retaining the original digest. Variant C contains the exact E1 bytes and digest but omits the source-selection and authority fields. For each variant, write (1) what the byte comparison supports, (2) which claim about R9/R14 remains blocked, (3) what you would ask the sender to supply and (4) whether the package is usable for a classroom copy-integrity demonstration versus an operational incident conclusion. This drill is separate from EX014–EX026 and uses no real data.

The acceptance criterion is deliberately narrow. Variant A can pass the classroom copy-integrity check if the reviewer repeats the calculation; it still cannot prove R14's underlying query was complete. Variant B fails the claimed byte identity, regardless of whether the change was accidental or deliberate; that causal question is not answered by the digest mismatch. Variant C can have identical bytes yet fail the provenance and authorization review. A simple five-point rubric awards one point for distinguishing A's two kinds of claim, one for correctly rejecting B's recorded digest match, one for C's missing provenance and authority, one for a specific repair request for each variant, and one for a handoff sentence that labels every unperformed check. A model comparison is given in the final appendix.

When preparing a real handoff, ask the next analyst to reproduce one consequential claim: locate its source pointer, re-run or review the approved selection, verify the copy if one exists, and identify the decision that would change if the claim fails. This is a quality check on the team's reasoning. It does not replace specialist evidence handling where legal, privacy, disciplinary or contractual obligations apply. Module 3 will use the same separation when it asks whether a successful backup job is also a tested and accepted recovery.

Distinguish source, copy and interpretation

A digest can support a byte-consistency comparison for a named object at named checks. It does not prove that the source exporter was authorized, that every relevant event was emitted, that the source clock was correct, or that the copied rows identify a human.

The existing E1 laboratory already proves the narrower mechanism: two equally sized classroom files with one changed byte produce different SHA-256 values. Repeating that code here would not add learning. Instead, audit whether a proposed conclusion relies on one of three independent layers:

Lesson table; scroll horizontally to read all columns →

Layer What it can support What remains to check
Source A producer is designated for the business question Its authority, schema, health, clocks and coverage
Copy A selected byte sequence and its integrity check are documented Source selection, permissions, transformations and missing rows
Interpretation The investigator relates records to hypotheses Identifier meanings, competing explanations and disconfirming evidence

A perfect digest at the copy layer cannot fill a gap in the source layer. Two files could be byte-identical copies of a truncated export. A complete export might still fail to identify the person behind a shared account label. A reviewer must be able to point to the exact layer checked rather than say vaguely that "the evidence is authentic."

A common false positive is to interpret R13's one-session summary as proof of a particular physical operator. R13 omits device and location details, and session counts alone do not authenticate the person at the keyboard. The existing Q1 exercise intentionally shows how matching identifiers and time windows can remain ambiguous; this proposal does not rewrite Q1 or the canonical R13.

Worked checkpoint

A matching digest demonstrates that the classroom copy still matches the digest used for comparison. It does not authenticate the original producer, fill a missing time window or prove that a requested source was actually retained.

Practice — Lesson 07

EX017 — What the summary cannot prove

Objective: test the limits of negative evidence. Inputs: R13, R14 and, for this question only, the optional T1 metadata. Output: two short claim/limit pairs and one proposed follow-up query. First assess R14 without T1; then state exactly what T1 adds and what it still cannot cover after 09:12. Explain why R13 does not establish that the desk-a session came from the expected staff device. A score of 2 distinguishes missing device fields from a negative device finding and names the query's filter and interval.

Extension: Before using optional T1, describe exactly which fields of R14's query envelope are missing. When using T1, distinguish a bounded event-time filter from an ingestion-completeness guarantee; name the first excluded boundary.

For full credit: The learner says that even optional T1 ends before 09:12 and does not authenticate logging completeness, another account or activity at and after 09:12. R13's missing device field remains unknown rather than a negative device finding.

Worked answer: EX017 in the final appendix.

EX019 — Collection note

Objective: make an exercise extract reviewable. Inputs: the E1 fixture in section 2.5 and R9/R14. Output: a collection-note table with case/item ID, source and event range, collector, authority, actual local collection time if a file was created, copy location, transformation and limitation. If no file was created, label the note planned and leave collection time and digest not applicable; do not pretend an export occurred. A score of 2 identifies E1 as a synthetic transcription, distinguishes original book text from the working file and records access scope. A made-up digest is not required.

State criterion within the existing 0–2 rubric: A completed local E1 file, a second-file failure and a planned collection are different states. Record only the file, time, integrity measurement, transfer or disposal actually evidenced; mark unrecorded or unperformed fields explicitly. Do not claim that failure to create the second file invalidated or erased an already created first file. A retained copy is not an applied source hold. This criterion adds no row-count requirement, exercise ID or production permission.

MIN2 supplementary task (after the required artifact):

Additional task: Keep E1's original provenance and state requirements. Add a separate planned derivative manifest: original source pointer, approved row interval/objects, fields retained, fields excluded, transformation version and intended recipient. Do not reuse E1's digest as if it authenticated new derived bytes; compute a new digest only if a local file was actually created and measured.

For full credit: Source and derived views are explicitly distinguished, and no collection/creation time or digest is fabricated. The original E1 transcript and its existing instructions remain unchanged.

HOLD2 supplementary task (after the required artifact): Preserve the original collection-note table for E1 and its actual-versus-planned status. Add a separate named source-hold ledger reference with its own proposal, authority, reported control, effect check and release/expiry fields. If no source hold was executed, mark these fields not applicable or not supplied, rather than repeating E1's copy hash as proof of source preservation.

For full credit: Source-control lifecycle and copied-item custody are distinct. The original E1 byte/transformation and access limits remain as written; no source hold, collection time or digest is fabricated.

Worked answer: EX019 in the final appendix.

EX020 — Hash claim

Objective: bound an integrity claim. Inputs: the named E1 bytes, the two digest checks and section 2.5. Output: a five-row claim table: two statements the comparison supports and three it does not, each with a one-sentence reason. Specify which file and times were checked rather than saying “the evidence is authentic.” A score of 2 distinguishes copy consistency from source truth, completeness and collection authority.

Extension: For one claim that a matching E1 digest supports, name its exact evidence layer (copy). For one claim it cannot support, name the missing source/selection or interpretation layer. Keep the E1 lab's original numbers and bytes unchanged.

For full credit: Matching hashes concern chosen bytes; they do not attest producer completeness, collection permission or the person responsible for B19.

MIN2 supplementary task (after the required artifact):

Additional task: Keep the existing five-row supported/unsupported hash-claim table. Add one sentence explaining why equal digests of a source copy do not authorize sharing the source's personal fields with a broader team, and why a projected view requires its own recorded transformation and limits.

For full credit: Byte consistency remains separate from source authenticity, completeness, permission and confidentiality. No statement that masking or hashing makes information automatically anonymous is accepted.

Worked answer: EX020 in the final appendix.

EX021 — Time ordering

Objective: order source-reported times without inventing operation, approval or collection times. Inputs: R9, R10, R12, R15 and R16; use T1 only for the optional final sentence. Output: exactly five ordered rows with ID, supplied UTC time, source and time kind, and one specific uncertainty or unsupported inference for that row. Mark the common 09:20 learner release separately from these source-card times; it does not supply local collection or source-ingestion times. Optionally state which interval T1 leaves uncovered, including its exclusive end. A score of 2 identifies the edit, plan-record, interview-note, retention-notice and service-owner-note times correctly, preserves the learner-release boundary, and does not turn record order into proof of authorization, attribution or a deletion deadline.

Worked answer: EX021 in the final appendix.

Lesson 08 — A source hold is a separate lifecycle

Week 2, day 3. Work through the teaching and the worked checkpoint before attempting the assigned exercise(s). Compare your artifact with the final worked result only afterward.

2.8a Verify a source preservation hold separately from a retained copy

2.8a.1 A request to preserve data is not the preservation itself

A responder may correctly recognize that useful source records could disappear. They can write an urgent, well-scoped request, obtain a decision from an accountable person and contact the custodian. None of those steps alone establishes that the underlying source has changed its deletion behavior.

This distinction is particularly important when a source's normal retention policy is short. In the fictional Harbor Library case, R15 reports that detailed booking-audit records are deleted after 24 hours unless an authorized copy is retained. R15 does not say whether the period begins at event time, ingestion time, storage time or another policy anchor. It also does not say that a source-level pause of deletion exists, is enabled, or has been requested. A responder must not add 24 hours to R9's 09:03 event time and publish an exact deletion deadline.

A preservation request asks that specified information be protected under the applicable authority and scope. A source hold—if supported by a particular system and authorized by the organization—changes or constrains the source's normal removal process for defined objects or intervals. A collected copy is an independently identified object held under its own access and retention rules. These are different things, even when the same custodial team manages them.

NIST SP 800-61 Rev. 3, especially RS.AN-06 and RS.AN-07, addresses records of investigative actions and the integrity, provenance, collection and retention of incident data. NIST SP 800-86 describes source-aware collection and preservation from an IT incident-response perspective, not as a legal preservation order. The specific hold lifecycle below is an original OpenCyber Academy teaching model; the cited sources do not prescribe its exact fields or claim that Harbor Library has such a capability.

2.8a.2 Separate three objects that may be confused

A useful incident note keeps three inventories rather than one ambiguous entry named "evidence saved":

Lesson table; scroll horizontally to read all columns →

Object What it represents What a positive entry does not prove
Source record set Events or data still maintained by the producing system or its governed storage layer That the events are complete, correct, or immune to every deletion path
Source-hold control A particular mechanism reported as protecting a defined source, selection and time interval That all records existed before activation, or that the hold covers an unrelated source
Collected evidence copy Bytes, export or image separately preserved at a specified location That the source's normal deletion was stopped, or that no source row was omitted

Suppose an authorized copy contains one booking-audit row and a SHA-256 digest matches at two checks. That may support a limited byte-consistency claim about the saved copy. It says nothing about whether the original application-audit source is still deleting rows. It also does not show that the copy contains every relevant source event.

Conversely, a confirmed source hold might keep selected records available without authorizing a broad export to the analyst. The custodian still needs permission, source-specific selection and a separate release or transfer procedure before supplying a copy. Protecting a source and collecting a reviewer copy can be complementary, but neither substitutes for the other.

The existing E1 laboratory teaches a digest-based check on a synthetic transcription; MIN2 teaches a restricted working view. HOLD2 does not repeat either. It asks whether the claimed source-level preservation effect was actually checked and what remains unproven even after a narrow positive check.

2.8a.3 Start with a decision question for P-A and P-B

At 09:20, the learner can propose two separate information paths. P-A concerns booking/application audit data relevant to the B19 edit and the limits of R14's earlier quiet-query summary. P-B concerns possible identity details that could clarify whether a session relationship is worth investigating. Their evidence value, custodians, privacy limits and loss concerns differ.

P-A is time-sensitive because R15 describes deletion of detailed booking-audit records after an unspecified 24-hour anchor. P-B may also be urgent, but the canonical record gives no identity-source retention period. An analyst should seek P-B availability information in parallel rather than borrow P-A's 24-hour rule and apply it to the identity service.

A proposed P-A preservation card should name which particular source is at risk. R9 is characterized as booking audit, while R14 is an application-audit summary. These labels must not be silently merged into one producer, one retention clock or one hold-control namespace. The custodian should confirm whether a mechanism applies to each and whether a separate request or receipt is needed.

The current investigation question might be: "Can we preserve the permitted detailed records needed to assess the B19 operation and its surrounding booking edits before the relevant source becomes unavailable?" A second, separate question asks whether limited identity details can be obtained with appropriate authority. Neither question requires collecting every patron or identity record.

The state of both P-A and P-B at the supplied Round B checkpoint is proposed. This supplement does not create a fictitious canonical approval or hold to move either card into a completed state.

2.8a.4 Define source-hold coverage as a contract, not a title

A ticket titled "hold B19 logs" is insufficient to know what would be protected. The receiving custodian needs an unambiguous control target and an authorized selection. A reviewable request should cover at least:

  1. Case and purpose: Which decision may depend on these records?
  2. Producing source: Exact storage/service and log or data collection identity, not merely "the logs".
  3. Objects: Booking IDs, event types or other identifiers within permitted scope.
  4. Time selection: Begin/end and the field used to interpret time, including whether endpoints are inclusive or exclusive.
  5. Authority: Who approved the hold request and under which organizational procedure and applicable restrictions?
  6. Custodian: Who can confirm the source's retention implementation and preserve any permitted copy?
  7. Timing: Requested start, reported effective start, permitted duration or review date, and any separate operational deadline.
  8. Intended mechanism and effect: Which deletion or retention operation should be constrained for the defined selection?
  9. Verification: What non-destructive source/control observations can challenge the claim that the hold is effective?
  10. Unresolved limits: Existing gaps, unprotected sources, unknown clock anchor and any unavailable oldest relevant row.

The field list is a teaching contract, not a legal specification or a promise that every logging system supports object-level holds. Some systems may offer retention settings, export-to-protected-storage workflows, snapshot protection or other mechanisms. They have different semantics, and a team must use the actual organization's approved procedures.

A hold on booking-audit is not necessarily a hold on application-audit or identity-detail. A hold covering 09:10–09:35 cannot establish coverage of a requested 09:00–09:30 interval. A hold covering one booking ID cannot establish coverage of every named object. Scope must be compared, not assumed from the presence of a control identifier.

2.8a.5 Approval and application produce different receipts

Suppose the fictional incident lead approves a P-A request to preserve a narrow source interval. The approval should name exactly what may be preserved and who will carry out the work. It does not establish that the custodian enabled a protective mechanism, or that the source supports one.

For a proposed operational workflow, use four initial receipt stages:

Requested. A bounded card exists. Its time and source show what was asked for, not that anyone agreed.

Approved. A responsible authority grants the defined operation and scope. The authorization may have restrictions, a deadline and a valid decision identifier. An approval for booking audit is not transferable to identity detail.

Application reported. The custodian records the applied control or other approved protective action, its target, time, identifier and intended behavior. This is an operator or system report, not an independently tested effect.

Effect checked. A reviewer assesses the reported source/control state and a bounded source-availability check against the approved selection. The result may support a limited effect claim if the method and sources are credible.

A custodian message saying "done" should therefore be followed by precise questions: done on which producer and collection, for which objects and time interval, using which control identifier, with what reported effective and review times? A positive receipt from the wrong source remains irrelevant to the requested one.

The HOLD2 script models these conditions as structured artificial fields. In a real case, a Boolean approved=True would not authenticate an actual person's decision. The team needs organizationally valid decision evidence and source-provided receipts, protected according to the applicable access and retention rules.

2.8a.6 Verify an effect without running an unsafe deletion test

A retention hold is often intended to prevent loss of data. It might appear that the simplest test is to try deleting a protected record. That can be unsafe, destructive, unauthorized or misleading. This course performs no deletion and does not recommend testing a real hold by deleting incident evidence.

For a preparation or incident review, the team can instead define non-destructive observations approved by the owner and custodian. Examples may include a governed control-status read, an audit entry recording the control's application, confirmation that the relevant retention/deletion job recognizes the protected selection, and a read-only retrieval of a known source row expected to exist.

The quality of those observations depends on what the actual source exposes. A dashboard saying "hold active" could be stale, refer to the wrong collection or omit a secondary deletion path. A successful retrieval of one older row proves that one row was accessible at that check time; it does not prove that all requested records are protected indefinitely.

A meaningful verification report should state the source and scope checked, who performed the check, the time and method, the recorded response, which expected row or control state was examined, and what could still invalidate the result.

The HOLD2 fixture uses synthetic flags for an "effect check" and an "oldest relevant row." Those are declarations inside a lab, not independent proof. The lab's strongest result is explicitly called bounded source hold confirmed in fixture, never "all deletion stopped in Harbor Library."

2.8a.7 The oldest requested row is a critical but limited sentinel

Why check an older row at all? If the request is intended to protect records already near a retention boundary, inspecting only a recent event may give false reassurance. A source might retain new rows while the oldest relevant data have already been removed.

The custodian should help identify a legitimate sentinel: a record expected to exist in the requested interval, with an identifiable source pointer and safe retrieval path. The analyst should not fabricate a row solely to make the test pass. An expected oldest row may be determined from a known record inventory, a reliable prior observation or another authorized source, subject to appropriate validation.

If the oldest relevant item cannot be retrieved, several explanations remain possible. It may have expired before the hold became effective; the source may never have generated it; a filter or index may exclude it; the request may target the wrong producer; or the hold may not cover it. The correct state is unverified coverage of that oldest item, not automatic proof of malicious deletion or proof that every part of the hold failed.

A custodian should document whether the row existed at the intended source before the request, the retrieval selection used, which storage tier was searched, and whether any source-internal transition occurred between application and check. If the cause cannot be resolved, the hold's claimed historical coverage must remain limited.

A future source-preservation mechanism cannot retroactively recreate data already removed. The team may consider authorized alternate sources or copies, but any resulting reconstruction must be documented separately from the original source hold.

2.8a.8 A hold can be valid now yet have a finite term

A source-protection mechanism may have a defined duration, expiration condition or required review. The record should separate:

A stated review date is not always the same as automatic technical expiry. A platform may continue enforcement after an administrative review is overdue, or may terminate enforcement at a lease boundary. The semantics depend on the source and control. The course must not invent them.

In HOLD2, the independent fixture provides a technical term_end. If the lab's assessment cutoff is at or after that exclusive term end, the program returns TERM_EXPIRED_EFFECT_UNKNOWN. It does not assert that a row was deleted or that deletion resumed immediately. Actual post-term source behavior requires its own authorized observation.

A valid hold receipt also should say what happens if review cannot be completed. A safe plan may require escalation before expiration, a separately approved renewal, or continuing safeguards as defined by the actual policy. The analyst cannot assume that a prior approval renews itself.

2.8a.9 Release and expiration must not erase the earlier lifecycle

An important archival principle applies even after the preservation reason has ended: historical decisions and receipts should remain reviewable within the organization's authorized recordkeeping rules. Closing the active hold state is not permission to rewrite its earlier existence.

A source-hold ledger can therefore append lifecycle entries such as requested, approved, application reported, effect checked, review due, renewed, release approved, release reported, release checked, or term-expired status recorded. Each entry should include the responsible role, event time, source and control identifier, and a reason or decision reference where appropriate.

Do not silently overwrite an earlier ACTIVE value with RELEASED if doing so destroys information about when protection was reported and what was tested before release. Keep a current summary and the historical transitions.

A release receipt is another distinct operational event. Someone may approve the release, the custodian may report that the mechanism was removed, and a reviewer may need to confirm the current source/control state. The release should not be tested by deleting evidence in an unauthorized manner.

The HOLD2 laboratory appends a fictitious RELEASE_RECORDED entry while retaining all earlier entries. The result is RELEASE_RECORDED_RECHECK_REQUIRED, deliberately avoiding the stronger claim that every deletion behavior after release was measured. The lab performs no deletion or modification of a source.

2.8a.10 The copy remains governed separately after a source hold ends

A particular collected copy may remain subject to the organization's evidence-retention rules after a source hold is released. Conversely, a copy may have its own permitted deletion or access conditions while a source-level hold remains active. Those are separate governance and custody decisions.

Consider two fictional ledger objects:

They should not share one ambiguous status. A checked digest for copy_item_id supports a limited byte-consistency statement, but not a source-control effect. A source-control status does not automatically authorize broad distribution of copy_item_id to analysts.

The current book already discusses source/copy/interpretation and a minimized technical-review view. HOLD2 contributes the lifecycle linkage: a hold record can point to a separately authorized copy without becoming that copy, and release of one should not silently alter the other's governance.

The AR1 authority-readiness exercise in Module 1 may help identify a valid approver under a separate pre-incident process. It does not replace the case-specific approval required for P-A or P-B; nor does it provide evidence that a hold was applied during Round B.

2.8a.11 Custodian unavailable: treat nonresponse as an escalation condition

A time-sensitive request may reach an organizational mailbox while the responsible custodian is unavailable. The responder must distinguish a dispatch attempt, an acknowledgement by a role, a custodian's confirmation of applicability and an actual application/effect receipt.

If the custodian has not responded, the hold remains not confirmed. The incident lead can use the organization's approved escalation route to seek an available, authorized custodian or service owner, explain the potential loss path and preserve existing permitted records. The team must not assume that leaving a voicemail, opening a ticket or copying a supervisor creates an active source hold.

The current R15 notice increases the importance of prompt clarification, but it does not supply a verified countdown from the 09:03 R9 event. A blocked request should record the absence of the custodian, time of attempts, what was asked, any ticket/reference, known evidence already at risk, next authorized contact and next review.

The analyst must not bypass source access controls or execute retention changes using unapproved credentials. An emergency retention procedure, if one exists in a real organization, requires its own documented permission boundary and receipt. This introductory exercise invents no such power.

The HOLD2 CUSTODIAN_UNAVAILABLE negative case returns that status rather than fabricating application or verification. It does not prove records were lost or that the custodian was negligent.

2.8a.12 Contrastive example — a wrong "hold complete" message

Incorrect synthetic Round B conclusion:

"R15 says the logs last 24 hours, so the B19 row will disappear at 09:03 tomorrow. P-A has been entered as urgent and someone copied R9 into a text file. The source hold is therefore active, all relevant booking logs are safe, and P-B identity data are covered by the same hold."

Every promotion of evidence in this statement is invalid. The retention anchor is unspecified; a ticket priority does not apply a control; a copy of one item does not stop source deletion; source coverage for all booking records has not been verified; and P-B is a different source with no supplied retention policy or hold receipt.

A source reviewer cannot repair the conclusion by calculating a different timestamp. The missing information is semantic: the policy anchor, actual custody/implementation path, scope and effect of a control, and whether the oldest relevant row remains available.

A valid next step would identify the bounded P-A source request, obtain an accountable decision, ask the custodian which mechanism if any can protect it, and document only the receipts actually returned. P-B proceeds as a separate availability inquiry with its own owner and scope.

2.8a.13 Correct example — a complete but explicitly hypothetical hold receipt

Entirely invented HOLD2 rehearsal, not a Harbor Library event. On the artificial date 2040-03-15, a team prepares a request for two artificial booking-audit items, BX-01 and BX-02, over an event-time interval from 09:00 inclusive to 09:30 exclusive.

A fictional incident lead is represented as having approved the request at 09:36. The specified application custodian reports applying a source-hold control at 09:40 to booking-audit-sim covering those two objects and an enclosing 08:55–09:35 interval. The declared technical term extends to 11:30, with a separate owner review to be scheduled under the teaching contract. At 10:00, a synthetic check reports that the relevant source-protection state was observed and that the expected oldest marker was retrievable in the selected toy source.

A careful fictional conclusion is:

"Within this bounded independent fixture, the named source-control receipt covers the requested objects and event interval, has the expected custodian and a reported activation, and the declared non-destructive check at 10:00 found the oldest marker available. The check supports only the specified source/objects and observed time. It does not prove that all earlier records existed, that every deletion mechanism is constrained, that any real system was held, or that the separate copy's retention was verified."

This positive example includes source, objects, interval, authority, custodian, effective time, limited term, check method and remaining uncertainty. It is correct because its claims stay within the fictitious evidence contract.

A different result is just as educational. If the receipt covers identity-detail-sim instead of booking-audit-sim, the program refuses source coverage even if every other field looks valid. If the oldest row cannot be retrieved, the team must investigate historical coverage rather than silently mark the hold complete. If the hold term has elapsed, the earlier positive check cannot establish continued current protection.

2.8a.14 A review decision must be different from a routine expiry

At a stated review time, the owner may decide to maintain, narrow, extend, release or escalate the hold under the organization's actual policy and legal requirements. The custodian may have technical facts about control capabilities, but those facts do not automatically grant authority to make a broader business or legal preservation decision.

A renewal should receive a new recorded decision and control confirmation. It should identify whether the original mechanism continued uninterrupted or whether a gap occurred; the earlier control ID and the new decision ID should both remain in the lifecycle. "Renewed" written in a case note does not prove a platform configuration changed.

A release decision should identify the rationale and responsible authority, the affected source and objects, any continuing preservation obligation, the custodian's reported action and the post-release verification status where applicable. It also must avoid treating source-release permission as permission to delete a separately collected copy.

An expiry record may be derived from comparing time with a declared technical term, but expiration of a term is not direct observation of actual deletion. A system could continue retaining data, apply a broader policy or have already lost historical rows. The correct state in an unverified source is "term expired; current effect unknown."

These distinctions make handoffs safer. The next analyst can see which source was protected during what interval, which claims were actually verified, and which new authorization is required before the team changes a preservation control.

2.8a.15 Two different negative source outcomes

A source hold can fail acceptance in at least two important ways:

Control-path problem. The requested source or interval is not protected by the reported control, the custodian is wrong, or the application/effect receipt is missing. Here the team should correct the control request or seek a valid custodian explanation, then repeat an authorized non-destructive check.

Historical-source problem. The control appears correctly scoped for current protection, but the oldest expected row is absent. Here the team must determine whether the row ever existed at that source, whether it was already removed before application, whether the retrieval path is incomplete, or whether another storage tier is relevant. It cannot conclude immediately that the hold mechanism failed for all future records.

A third category is unknown coverage: the source does not expose enough status or row-level evidence to demonstrate the intended protection. In that case the reviewer can report that the custodian's statement was recorded but effect remains unverified, rather than replacing missing proof with an optimistic assumption.

These outcomes are different from VQ2. VQ2 concerns whether a query result supports a claim about event coverage under filters and ingestion watermark. HOLD2 concerns whether an authorized preservation control was applied to the intended source and selection, with a reviewable lifecycle. A valid hold does not make a poorly scoped query complete; a valid query does not make a source hold effective.

2.8a.15a Accept a linked evidence chain, not a collection of reassuring fields

A positive result needs more than a source name and several true flags. It needs agreement between the request, the permission actually granted, the reported control, and the observation used to assess that control. The purpose of this check is not to add paperwork: it is to prevent a valid receipt for one operation from answering a different question.

Start with three boundaries. The requested scope contains the records needed for the decision. The reported coverage describes the objects and event interval the custodian says the control covers. The authorized scope is the maximum selection the approving role permitted. For this exercise, require requested scope to fit inside reported coverage, and reported coverage to fit inside authorized scope. Check source, objects and time interval separately. Covering every requested object is not sufficient if the reported operation also includes an unapproved object.

In HOLD2, the requested event window is 09:00–09:30, while the reported control covers 08:55–09:35. The larger interval can be acceptable only with permission for that larger selection. The revised fixture supplies an explicit, invented authorization for 08:55–09:35 and BX-01/BX-02. This is additional classroom metadata, not a newly discovered Harbor Library approval. Expanding the receipt to 08:50 or adding BX-99 exceeds that permission even though the original requested records would still be covered. An empty request is rejected: mathematically, an empty set fits inside any set, but it cannot establish which evidence a preservation decision intended to protect.

Next bind the identifiers. The authorization and control receipt both name HOLD2-REQ-01; the recorded effect check names HOLD2-CTRL-01 and the same source. An approval for OTHER-REQUEST cannot be reused merely because its source name looks familiar. A result for another control cannot establish this control's effect. These comparisons detect declared inconsistencies; they cannot authenticate the issuer, prevent a forger from changing every field consistently, or establish that the named role was truly appointed.

Then inspect the meaning and order of lifecycle steps, not just whether their timestamps increase. The modeled path is request, approval, application reported and effect checked, optionally followed by release recorded. A timestamp-sorted list containing only REQUESTED is not a completed lifecycle. Nor is request, application, approval, check a valid prior-approval path simply because the timestamps increase. Under this fixture's single-application contract, the approval time must agree with its history entry, and the receipt's activation time must agree with APPLIED_REPORTED. A missing step remains missing; do not manufacture a receipt to make the sequence pass. Systems with renewals, multiple applications or different reporting-time semantics need a different documented model.

Finally, identify the observation. BX-01 is the declared sentinel for this exercise; retrieving BX-02 does not answer whether BX-01 remains available. Its selection as the oldest relevant marker is a supplied teaching premise, not something the program derives from a complete source inventory. Even a correctly bound retrieval establishes only that the selected marker was reported accessible at the recorded check. It does not demonstrate that every deletion path is constrained, every historical row existed, or every relevant object remains present.

Lesson table; scroll horizontally to read all columns →

Review question Specific check in the fixture What remains unproved
Was this selection permitted? Requested scope fits reported coverage, which fits the named authorization; role and operation agree Authentic issuance, current organizational authority and real platform permissions
Do the receipts describe one operation? Request IDs, source, control ID and modeled lifecycle agree Whether an authentic operator actually performed it
Was this the intended observation? Effect source/control and declared sentinel match Source completeness, a real protection mechanism or the reason for an absent row
Does a later review establish present protection? as_of bounds available history and checks the stated term Continued effectiveness since the last observation

as_of is an information cutoff, not a new observation. Moving it from 10:00 to 10:15 without new source evidence does not create a fresh 10:15 effect check. At the exact 11:30 term boundary, the active-term claim ends under this fixture's exclusive-end convention; the result says that current effect is unknown, not that data were deleted. A recorded release likewise requires a new authorized state check. This program records release history but does not authenticate release permission or perform release; it cannot certify that a release was lawful or technically effective.

These checks implement an original OpenCyber Academy teaching contract. NIST SP 800-61 Rev. 3, RS.AN-06 and RS.AN-07, page 29 supports recording investigative actions, safeguarding provenance and integrity, and collecting and retaining evidence under organizational procedures. It does not prescribe these identifiers, set comparisons, time limits or result labels. NIST SP 800-86 also expressly limits its forensic guidance: it is not an all-inclusive investigation procedure or legal advice.

Apply the reasoning before running Python. For EX018, keep the three-row P-A request and two-row P-A/P-B comparison; use their existing authority and receipt fields to ask for a request-bound permission, the permitted selection and the matching application/effect record. For EX019, keep E1's collection note separate from any source-control reference. For EX022, retain the five-sentence handoff and its proposed status. For EX024, keep two hypothetical trigger/response pairs: an inconsistent receipt calls for clarification and a new authorized review, not automatic containment or proof of deletion. All unprovided P-A/P-B details remain pending at 09:20. No HOLD2 identifier, timestamp or passing run belongs in R1–R16.

2.8a.16 Section synthesis — preserve the source, the copy and the decision trail separately

The learner should finish with three defensible habits. First, name the source, authorized objects and interval precisely enough that a custodian can say what is and is not protected. Second, separate requested, approved, applied and verified states, and use non-destructive source observations to bound any effect claim. Third, keep a versioned or append-only lifecycle through review, expiry and release without erasing earlier decisions or equating a protected copy with protected source retention.

For Harbor Library at 09:20, P-A and P-B remain proposed. R15 leaves the exact deletion anchor unknown. No fictional HOLD2 positive result, expiry or release may be imported into R1–R16. The pending task is to obtain an authorized, narrowly scoped custodian clarification and document whatever the real source could verify under its actual procedures.

2.8b HOLD2 offline laboratory — source hold lifecycle and coverage

Environment: Python 3 standard library, entirely in memory. No file creation, network access, credentials, data collection, retention-setting changes, destructive probes or deletion.

Fixture classification: A separate artificial 2040-03-15 scenario; the source names booking-audit-sim and identity-detail-sim, objects BX-01/BX-02 and hold receipts are invented. They are not R-series records or real P-A/P-B execution evidence. Goal: Decide whether a described source hold has sufficient bounded simulated source/custodian/scope/effect evidence, and refuse broader conclusions when a required step is missing or a term has elapsed.

Assumptions and observations to predict

A request covers two artificial records in the interval 09:00 ≤ event < 09:30 UTC. An invented approving role has accepted its scoped source, and the invented application custodian reports applying a control at 09:40 that covers 08:55–09:35. The fixture checks state at 10:00 with an artificial effect-check flag and oldest-row availability flag. Its reported technical term ends at 11:30.

The program verifies declared consistency and prints a reason when it refuses acceptance. The Boolean fields are invented teaching assertions, not authenticated approval, actual source protection or proof of deletion prevention.

Complete Python 3 program

Execution contract: Run this whole listing in a fresh Python 3 session, not after another laboratory that reuses BASE or utc. It prints 33 lines: the previous sixteen baseline lines, three chronology controls and fourteen additional acceptance controls. All times belong to the artificial UTC date 2040-03-15. The explicit authorization, request/control IDs and sentinel binding are invented additions to HOLD2 only. They do not authenticate an issuer or add a case approval to R1–R16.

from copy import deepcopy
from datetime import datetime, timedelta
import re

def utc(value):
    return datetime.fromisoformat(
        f"2040-03-15T{value}:00+00:00"
    )

REQUEST = {
    "id": "HOLD2-REQ-01",
    "sentinel": "BX-01",
    "source": "booking-audit-sim",
    "objects": {"BX-01", "BX-02"},
    "interval": (utc("09:00"), utc("09:30")),
    "custodian": "AppCustodian",
}
RECEIPT = {
    "id": "HOLD2-CTRL-01",
    "request_id": "HOLD2-REQ-01",
    "source": "booking-audit-sim",
    "objects": {"BX-01", "BX-02"},
    "interval": (utc("08:55"), utc("09:35")),
    "custodian": "AppCustodian",
    "active_from": utc("09:40"),
    "term_end": utc("11:30"),
}
AUTHORIZATION = {
    "request_id": "HOLD2-REQ-01",
    "operation": "hold_source",
    "source": "booking-audit-sim",
    "objects": {"BX-01", "BX-02"},
    "interval": (utc("08:55"), utc("09:35")),
    "approver": "IncidentLead",
    "custodian": "AppCustodian",
    "approved_at": utc("09:36"),
    "valid_until": utc("11:30"),
}
BASE = {
    "authorization": AUTHORIZATION,
    "effect_control_id": "HOLD2-CTRL-01",
    "effect_source": "booking-audit-sim",
    "oldest_checked": "BX-01",
    "request": REQUEST,
    "approved": True,
    "approval_source": "booking-audit-sim",
    "receipt": RECEIPT,
    "custodian_available": True,
    "effect_checked": True,
    "oldest_retrievable": True,
    "as_of": utc("10:00"),
    "history": (
        ("REQUESTED", "09:34"),
        ("APPROVED", "09:36"),
        ("APPLIED_REPORTED", "09:40"),
        ("EFFECT_CHECKED", "10:00"),
    ),
    "copy_retained": False,
}

def input_problem(s):
    """Check the fixed teaching schema; never authenticate a real source."""
    if not isinstance(s, dict) or set(s) != set(BASE):
        return "INVALID_PACKET_SCHEMA"
    for field in ("approved", "custodian_available", "effect_checked",
                  "oldest_retrievable", "copy_retained"):
        if type(s[field]) is not bool:
            return "NON_BOOLEAN:" + field
    for field in ("approval_source", "effect_control_id", "effect_source",
                  "oldest_checked"):
        if not isinstance(s[field], str) or not s[field]:
            return "INVALID_IDENTIFIER:" + field
    objects = (("request", REQUEST), ("authorization", AUTHORIZATION))
    if s["receipt"] is not None:
        objects += (("receipt", RECEIPT),)
    times = [s["as_of"]]
    for name, model in objects:
        item = s[name]
        if not isinstance(item, dict) or set(item) != set(model):
            return "INVALID_OBJECT_SCHEMA:" + name
        for field, value in item.items():
            if isinstance(model[field], str):
                if not isinstance(value, str) or not value:
                    return "INVALID_IDENTIFIER:" + name + "." + field
        selected = item["objects"]
        if (not isinstance(selected, (set, frozenset)) or not selected
                or any(not isinstance(x, str) or not x for x in selected)):
            return "INVALID_OBJECT_SCOPE:" + name
        window = item["interval"]
        if not isinstance(window, tuple) or len(window) != 2:
            return "INVALID_INTERVAL:" + name
        times.extend(window)
    times.extend((s["authorization"]["approved_at"],
                  s["authorization"]["valid_until"]))
    if s["receipt"] is not None:
        times.extend((s["receipt"]["active_from"], s["receipt"]["term_end"]))
    for value in times:
        if (not isinstance(value, datetime) or value.tzinfo is None
                or value.utcoffset() != timedelta(0)
                or value.date().isoformat() != "2040-03-15"):
            return "INVALID_UTC_TIME"
    for name, _ in objects:
        if s[name]["interval"][0] >= s[name]["interval"][1]:
            return "INVALID_INTERVAL:" + name
    if not isinstance(s["history"], tuple):
        return "INVALID_HISTORY_SCHEMA"
    for entry in s["history"]:
        if (not isinstance(entry, tuple) or len(entry) != 2
                or not all(isinstance(x, str) for x in entry)):
            return "INVALID_HISTORY_SCHEMA"
        if not re.fullmatch(r"[0-9]{2}:[0-9]{2}", entry[1]):
            return "INVALID_HISTORY_CLOCK"
        try:
            utc(entry[1])
        except ValueError:
            return "INVALID_HISTORY_CLOCK"
    return None


def status(s):
    problem = input_problem(s)
    if problem is not None:
        return problem
    req, rec = s["request"], s["receipt"]

    if not s["approved"]:
        return "PROPOSED_ONLY"
    if s["approval_source"] != req["source"]:
        return "APPROVAL_NOT_FOR_SOURCE"
    if not s["custodian_available"]:
        return "CUSTODIAN_UNAVAILABLE"
    if rec is None:
        return "APPROVED_APPLICATION_UNCONFIRMED"
    if rec["source"] != req["source"]:
        return "WRONG_SOURCE"
    if rec["custodian"] != req["custodian"]:
        return "WRONG_CUSTODIAN"
    if (
        not req["objects"].issubset(rec["objects"])
        or rec["interval"][0] > req["interval"][0]
        or rec["interval"][1] < req["interval"][1]
    ):
        return "SCOPE_GAP"
    if s["as_of"] < rec["active_from"]:
        return "NOT_YET_ACTIVE"

    if rec["term_end"] <= rec["active_from"]:
        return "INVALID_CONTROL_TERM"
    trail = [(kind, utc(clock)) for kind, clock in s["history"]]
    if (
        not trail
        or any(trail[i][1] > trail[i + 1][1]
               for i in range(len(trail) - 1))
        or any(when > s["as_of"] for _, when in trail)
    ):
        return "HISTORY_NOT_AVAILABLE_AS_OF"

    authorization = s["authorization"]
    if (authorization["request_id"] != req["id"]
            or rec["request_id"] != req["id"]):
        return "REQUEST_BINDING_MISMATCH"
    if (authorization["source"] != req["source"]
            or authorization["operation"] != "hold_source"):
        return "AUTHORIZATION_TARGET_MISMATCH"
    if (authorization["approver"] != "IncidentLead"
            or authorization["custodian"] != req["custodian"]):
        return "AUTHORIZATION_ROLE_MISMATCH"
    if (not rec["objects"].issubset(authorization["objects"])
            or rec["interval"][0] < authorization["interval"][0]
            or rec["interval"][1] > authorization["interval"][1]):
        return "AUTHORIZED_SCOPE_EXCEEDED"
    if (authorization["approved_at"] > rec["active_from"]
            or authorization["valid_until"] < rec["term_end"]):
        return "AUTHORIZATION_TIME_MISMATCH"
    expected = ("REQUESTED", "APPROVED", "APPLIED_REPORTED", "EFFECT_CHECKED")
    kinds = tuple(kind for kind, _ in trail)
    if kinds not in (expected, expected + ("RELEASE_RECORDED",)):
        return "HISTORY_LIFECYCLE_MISMATCH"
    if trail[1][1] != authorization["approved_at"]:
        return "APPROVAL_TIME_MISMATCH"
    if trail[2][1] != rec["active_from"]:
        return "ACTIVATION_TIME_MISMATCH"
    if (s["effect_control_id"] != rec["id"]
            or s["effect_source"] != rec["source"]):
        return "EFFECT_BINDING_MISMATCH"
    if (req["sentinel"] not in req["objects"]
            or s["oldest_checked"] != req["sentinel"]):
        return "SENTINEL_BINDING_MISMATCH"

    # Preserve earlier steps; a release is an appended event.
    if "RELEASE_RECORDED" in [
        kind for kind, _ in s["history"]
    ]:
        return "RELEASE_RECORDED_RECHECK_REQUIRED"
    if s["as_of"] >= rec["term_end"]:
        return "TERM_EXPIRED_EFFECT_UNKNOWN"
    if not s["effect_checked"]:
        return "EFFECT_NOT_VERIFIED"
    if not s["oldest_retrievable"]:
        return "OLDEST_ROW_UNAVAILABLE"

    return "BOUNDED_SOURCE_HOLD_CONFIRMED_IN_FIXTURE"

def variant(changes):
    row = deepcopy(BASE)
    row.update(changes)
    return row

CASES = [
    ("VALID_BOUND", {}),
    ("PROPOSED_ONLY", {"approved": False, "receipt": None}),
    ("APPROVED_NO_APPLY", {"receipt": None}),
    (
        "COPY_ONLY",
        {"approved": False, "receipt": None,
         "copy_retained": True},
    ),
    (
        "APPROVAL_WRONG_SOURCE",
        {"approval_source": "identity-detail-sim"},
    ),
    (
        "RECEIPT_WRONG_SOURCE",
        {"receipt": {
            **RECEIPT, "source": "identity-detail-sim"
        }},
    ),
    (
        "RECEIPT_SCOPE_GAP",
        {"receipt": {
            **RECEIPT, "objects": {"BX-01"}
        }},
    ),
    (
        "WRONG_CUSTODIAN",
        {"receipt": {
            **RECEIPT, "custodian": "OtherCustodian"
        }},
    ),
    (
        "CUSTODIAN_UNAVAILABLE",
        {"custodian_available": False},
    ),
    (
        "OLDEST_MISSING",
        {"oldest_retrievable": False},
    ),
    (
        "NO_EFFECT_PROBE",
        {"effect_checked": False},
    ),
    (
        "NOT_YET_ACTIVE",
        {"as_of": utc("09:38")},
    ),
    (
        "TERM_EXPIRED",
        {"as_of": utc("11:45")},
    ),
    (
        "RECEIPT_WINDOW_GAP",
        {"receipt": {
            **RECEIPT,
            "interval": (utc("09:10"), utc("09:35"))
        }},
    ),
    (
        "RELEASE_RECORDED",
        {
            "as_of": utc("10:15"),
            "history": BASE["history"] + (
                ("RELEASE_RECORDED", "10:05"),
            ),
        },
    ),
]

for label, changes in CASES:
    print(f"{label}: {status(variant(changes))}")

print(
    "HISTORY_RELEASE:",
    " -> ".join(
        kind for kind, _ in CASES[-1][1]["history"]
    ),
)

assert (
    status(variant({}))
    == "BOUNDED_SOURCE_HOLD_CONFIRMED_IN_FIXTURE"
)
assert (
    status(variant(CASES[-1][1]))
    == "RELEASE_RECORDED_RECHECK_REQUIRED"
)

# Negative controls remain outside the R-series case record.
future_release = variant({
    "history": BASE["history"] + (("RELEASE_RECORDED", "10:05"),)
})
early_review = variant({"as_of": utc("09:45")})
invalid_term = variant({
    "receipt": {**RECEIPT, "term_end": utc("09:35")}
})
for label, sample in (
    ("FUTURE_RELEASE", future_release),
    ("FUTURE_EFFECT_CHECK", early_review),
    ("INVALID_TERM", invalid_term),
):
    print(f"{label}: {status(sample)}")
assert status(future_release) == "HISTORY_NOT_AVAILABLE_AS_OF"
assert status(early_review) == "HISTORY_NOT_AVAILABLE_AS_OF"
assert status(invalid_term) == "INVALID_CONTROL_TERM"

# Additional independent acceptance controls, not R-series events.
CONTROLS = [
    ("EMPTY_OBJECTS", {"request": {**REQUEST, "objects": set()}},
     "INVALID_OBJECT_SCOPE:request"),
    ("REVERSED_INTERVAL", {"request": {**REQUEST,
        "interval": (utc("09:25"), utc("09:05"))}},
     "INVALID_INTERVAL:request"),
    ("TEXT_FALSE", {"approved": "False"}, "NON_BOOLEAN:approved"),
    ("MISSING_APPLICATION", {"history": (
        ("REQUESTED", "09:34"), ("APPROVED", "09:36"),
        ("EFFECT_CHECKED", "10:00"))}, "HISTORY_LIFECYCLE_MISMATCH"),
    ("APPROVAL_AFTER_APPLY", {"authorization": {
        **AUTHORIZATION, "approved_at": utc("09:45")}},
     "AUTHORIZATION_TIME_MISMATCH"),
    ("REUSED_APPROVAL", {"authorization": {
        **AUTHORIZATION, "request_id": "OTHER-REQUEST"}},
     "REQUEST_BINDING_MISMATCH"),
    ("EXTRA_OBJECT", {"receipt": {
        **RECEIPT, "objects": {"BX-01", "BX-02", "BX-99"}}},
     "AUTHORIZED_SCOPE_EXCEEDED"),
    ("WIDER_INTERVAL", {"receipt": {**RECEIPT,
        "interval": (utc("08:50"), utc("09:35"))}},
     "AUTHORIZED_SCOPE_EXCEEDED"),
    ("WRONG_EFFECT_CONTROL", {"effect_control_id": "OTHER-CONTROL"},
     "EFFECT_BINDING_MISMATCH"),
    ("WRONG_SENTINEL", {"oldest_checked": "BX-02"},
     "SENTINEL_BINDING_MISMATCH"),
    ("ACTIVATION_DISAGREES", {"receipt": {
        **RECEIPT, "active_from": utc("09:41")}},
     "ACTIVATION_TIME_MISMATCH"),
    ("NAIVE_REVIEW", {"as_of": datetime(2040, 3, 15, 10)},
     "INVALID_UTC_TIME"),
    ("INVALID_HISTORY_CLOCK", {"history": (("REQUESTED", "25:99"),)},
     "INVALID_HISTORY_CLOCK"),
    ("EXACT_TERM_END", {"as_of": utc("11:30")},
     "TERM_EXPIRED_EFFECT_UNKNOWN"),
]
for label, changes, expected in CONTROLS:
    actual = status(variant(changes))
    assert actual == expected, (label, actual, expected)
    print(f"{label}: {actual}")

Before reading the final appendix, predict all 33 output lines: sixteen baseline lines, three chronology controls and fourteen acceptance controls. Explain the first blocking condition for each variant. Compare the actual run with the HOLD2 worked result.

Required learner artifacts: Reproduce the 33 output lines; create a scenario table with the first blocking condition and a safe next request; identify why COPY_ONLY does not establish source hold and why OLDEST_MISSING does not prove deletion timing; compare requested, reported and authorized scope; identify the request/control/sentinel bindings and required lifecycle steps; propose a reviewer check for release and expiry that does not erase history or delete data. A future lifecycle entry cannot support an earlier as_of conclusion; even a consistent fixture does not authenticate a real hold.

Five-point assessment for this unnumbered lab: One point each for exact output; correct source/object/interval coverage and authorization reasoning; separation of copy from source control; correct treatment of unavailable custodian or a specifically identified oldest row; and complete request-bound lifecycle, expiry/release history and explicit effect limits. Assess the added controls within these same five criteria. The lab adds no new EX identifier.

HOLD2-R — A review deadline is not a new source observation

Learning goal: Write a hold-review note that separates the age of the last reported observation, an owner-review appointment and the end of a stated technical term. Then explain why a first-refusal label is not a complete defect inventory. Complete the 33-line HOLD2 program above before this checkpoint; its inputs, outputs and five-point assessment remain unchanged.

Independent review metadata: For this checkpoint only, add an invented owner-review appointment at 10:15 UTC on 2040-03-15 for HOLD2-CTRL-01. It is a scheduled decision, not a completed review, a new authorization, a source query or a renewal. Keep it outside the fixed HOLD2 packet schema. All existing source, object and receipt values remain the artificial HOLD2 values, not Harbor Library evidence.

Read three clocks without turning one into another

The requested event interval selects which records matter. Activation and the technical term describe a reported control. The last effect-check time dates the supplied observation. An owner-review appointment says when a decision is due. A later as_of selects the information cutoff at which the packet is assessed; it cannot manufacture a new source observation.

Event selection: [09:00, 09:30)       Which source events are requested?
Reported control:       09:40 -------------------------- 11:30 excluded
Reported effect check:          10:00
Owner review due:                       10:15
Later assessment:                              11:29
                                      same 10:00 observation; age 89 min

Text alternative: The event interval ends at 09:30; reported activation is at 09:40; the only reported effect check is at 10:00. The additional owner review is due at 10:15. At an 11:29 assessment, the observation is 89 minutes old even though the stated technical term has not reached its exclusive 11:30 end. This is a logical ordering, not a scale drawing or proof of continuous protection.

Consider the claim: "The 11:29 run returned a positive label, so the source was checked again at 11:29." The calculation is reproducible, but the claim changes its meaning. HOLD2 checks the consistency of the same supplied packet. Its last reported effect observation remains 10:00. The correct age is 11:29 - 10:00 = 89 minutes. That arithmetic measures neither uninterrupted hold effectiveness nor a probability that protection has failed.

At 10:15, a different question becomes due: has the owner made the scheduled review decision? No review-completion receipt is supplied. The appointment does not expire the technical mechanism, renew its authority, authorize a release or refresh the source observation. At 11:30 exactly, however, the fixed HOLD2 term ends under the exclusive-end convention and the original function returns TERM_EXPIRED_EFFECT_UNKNOWN. The historical 10:00 check remains recorded. Neither this label nor an overdue appointment proves that data were deleted.

There is no universal maximum acceptable observation age in this fixture. A real decision would need an owner-defined review procedure, source capabilities, relevant change signals and authorized new observations. Do not invent a fifteen-minute freshness policy merely because this particular appointment is fifteen minutes after the check. The checkpoint supplies an appointment, not a measured risk threshold.

Worked review wording: "At the artificial 10:15 assessment, the packet still contains only the reported 10:00 effect check for HOLD2-CTRL-01 on booking-audit-sim; that observation is fifteen minutes old. The additional owner review is due, with no completed review or new source check supplied, while the recorded technical term ends at 11:30 exclusive. Seek the owner's decision and an appropriately authorized, source-bound current-state check; do not report renewal, current protection or deletion as observed."

This note identifies what the next reviewer must obtain without silently replacing the earlier record. In the original FUTURE_EFFECT_CHECK control, setting as_of to 09:45 while retaining the 10:00 history correctly fails. Neither moving the cutoff forward nor deleting an inconvenient history row supplies missing evidence.

A first refusal is not a complete diagnosis

The function uses ordered return statements. Python's function documentation explains that returning a value exits that function call. Once HOLD2 reports WRONG_SOURCE, later coverage, authorization and observation checks have not all been evaluated. The label identifies the first blocking condition under this program, not the only problem in the packet or a complete operational state.

For example, a deliberately defective receipt can name the wrong source, include unapproved BX-99, and provide neither a verified-effect assertion nor an available-sentinel assertion. Changing only the source in a separately labeled hypothetical packet exposes the next block; it does not establish that a custodian corrected a real control. Even after the object scope is aligned, missing effect evidence and an unavailable sentinel remain different questions. A negative result should be accompanied by the known outstanding issues and the receipts needed to resolve them, not by an invented sequence of completed repairs.

The original negative variants sometimes retain other baseline fields so that one early guard can be studied. Their reassuring or conflicting later fields cannot override the refusal. Do not use the label PROPOSED_ONLY, for example, as proof that an inherited history was exhaustively checked and contains no application entry. HOLD2 is a first-refusal consistency checker for this teaching contract, not a production lifecycle reconstruction engine.

Run the independent predictions in the HOLD2 session

Run the complete program above in a fresh session, then run this continuation in that same session. It prints eight additional lines and does not redefine status, change BASE, write files, contact a source or perform a hold/release. Its first four packets change only the assessment cutoff. Its four MULTI packets are independent hypothetical inputs, not a record of an operator fixing a source. The additional review appointment is deliberately not inserted into the strict original schema.

# Run after the complete HOLD2 listing, in the same fresh session.
# The review appointment is additional invented metadata, not a receipt.
owner_review_due = utc("10:15")
frozen_base = deepcopy(BASE)
assert status(BASE) == "BOUNDED_SOURCE_HOLD_CONFIRMED_IN_FIXTURE"
effect_times = [utc(clock) for kind, clock in BASE["history"]
                if kind == "EFFECT_CHECKED"]
assert len(effect_times) == 1
last_check = effect_times[0]

for clock in ("10:00", "10:15", "11:29", "11:30"):
    sample = variant({"as_of": utc(clock)})
    age_minutes = (sample["as_of"] - last_check) // timedelta(minutes=1)
    due = "DUE" if sample["as_of"] >= owner_review_due else "NOT_DUE"
    print(f"REVIEW {clock}: last_check={last_check:%H:%M} "
          f"age_min={age_minutes} owner_review={due} "
          f"result={status(sample)}")

# Four separate hypothetical packets, not successive operational repairs.
for label, wrong_source, extra_object, effect in (
    ("MULTI_A", True, True, False),
    ("MULTI_B", False, True, False),
    ("MULTI_C", False, False, False),
    ("MULTI_D", False, False, True),
):
    sample = variant({"effect_checked": effect,
                      "oldest_retrievable": False})
    if wrong_source:
        sample["receipt"]["source"] = "identity-detail-sim"
    if extra_object:
        sample["receipt"]["objects"].add("BX-99")
    print(f"{label}: {status(sample)}")

assert BASE == frozen_base  # The original packet and its history are unchanged.

The age calculation uses floor division by a one-minute timedelta; the quotient is exact for these minute-aligned inputs. Python documents duration arithmetic. No system clock is read. DUE means the invented appointment has been reached, with no completion supplied; it is not an observation of the control. Adding owner_review_due to BASE would instead violate the original fixed schema.

Independent practice: Before running, prepare a four-row review table for 10:00, 10:15, 11:29 and 11:30, with the last reported check, its age, owner-review status, term status and one unsupported claim. Then prepare four MULTI rows naming the first refusal, at least one remaining declared issue when present, and the specific authorized clarification needed. Explain why a corrected receipt must be preserved as a new version and reviewed again in full, rather than silently editing the prior input until it passes.

Finally, write two sentences correcting "the 11:29 positive result proves current protection" and "fixing MULTI_A's source proves every other field passed." For EX018, keep the existing three-row P-A request and two-row P-A/P-B comparison; its custodian-question field can ask separately for the last observed effect, the next owner review and the technical term's actual semantics. In canonical Round B, those hold details remain not supplied, both requests remain proposed, R15's retention anchor remains unknown and P-B has no supplied retention duration. Do not borrow the HOLD2-R appointment or term.

Assess these explanations within the existing HOLD2 criteria for output, scoped authorization, copy/source separation, missing observations and lifecycle limits. A full-credit answer keeps all three clocks distinct and identifies untested or unresolved conditions beyond the first diagnostic. This checkpoint adds no daily lesson or numbered exercise. Worked checkpoint answer.

2.8c Practical two-role activity — custodian packet and independent review

Purpose: Demonstrate that a second person can distinguish a requested, approved, applied and verified source hold without relying on the packet author's explanation.

Roles: Custodian / Packet Author and Independent Reviewer. The incident-lead approval is represented only by a fictional classroom request card. There is no real source custodian, logging system or deletion mechanism involved.

Step 1 — Freeze the source-hold request

The packet author creates HOLD2-request-v1 with the fictional source, object IDs, time interval, purpose, proposed approval, custodian, requested hold mechanism, earliest relevant row and expected review terms. Mark the actual custodian acknowledgement, control identifier, effect check and observed oldest-row state not supplied until the artificial receipt card is given.

Keep a separate copy-item card, if using one, with its own retention and integrity fields. Do not allow a saved copy to change the source-hold status.

Step 2 — Prepare and freeze one simulated custodian response

Choose the valid HOLD2 fixture or a wrong-source, oldest-missing, reused-approval or wrong-effect-control variant. Record the requested selection, the explicit authorization envelope, request and control IDs, application time, technical term, declared sentinel, check response and missing fields. Keep the complete modeled lifecycle and distinguish an unverified issuer from a structurally matching identifier. Freeze the packet as HOLD2-custodian-v1; a positive script result does not replace the receiver's independent reconstruction.

The reviewer must not see the author's private reasoning or a corrected answer. The packet may include explicit approved source pointers and the relevant original teaching rules.

Step 3 — Independent review with no author assistance

The reviewer receives the two frozen cards and must answer:

  1. What was requested, and was the request approved for that exact source and selection?
  2. Which source, objects and interval did the custodian report protecting?
  3. Does the receipt identify the appropriate custodial role, activation time, duration or review condition?
  4. Which non-destructive effect observation is provided, and which expected oldest source row was or was not retrieved?
  5. Is the current state confirmed for this narrow scope, merely reported, unverified, expired, released, or awaiting escalation?
  6. What is the safest next authorized request, and what claim remains unsupported?
  7. Is there a separately retained copy, and why does it not settle the source-hold question?

The reviewer saves HOLD2-review-v1 before discussion. Missing fields must remain missing. The reviewer should not claim that a source was deleted or that a hold succeeded solely because the author used reassuring wording.

Step 4 — Evaluate, then correct with a new version

Compare the frozen response with the request and receipt. Identify whether a discrepancy came from the packet's missing field, the reviewer's interpretation, or an unavailable custodian/source. Retain the original versions. If a change is needed, write HOLD2-request-v2 or HOLD2-custodian-v2 and run another separately marked review; do not rewrite the first attempt.

Pass: The reviewer locates the source/scope, distinguishes proposed/approved/applied/verified states, recognizes the meaning of the oldest-item check, does not calculate a deadline from R15's unknown anchor, and chooses a safe hold/escalation or recheck.

Critical fail: The reviewer calls a proposed request an active hold, treats a copy as evidence that deletion stopped, accepts a wrong-source/wrong-object receipt, assumes an absent oldest row was deleted at an invented time, or authorizes an intrusive test without permission.

Limit: This tabletop and the Python output measure only consistency and independent reasoning on synthetic records. They do not prove organizational readiness, actual source preservation, legal compliance or completion of any Harbor Library case action.

Worked checkpoint

A custodian may acknowledge a hold request while its effect remains unverified. The case record should show request, approval, application report, effect check and expiry separately; a downloaded copy has its own custody trail.

Practice — Lesson 08

EX018 — Retention decision

Objective: prepare a proportionate preservation decision. Inputs: R9, R13–R15, the collection-note fields in section 2.3 and the worked queue in section 2.4. Output: a three-row P-A request to the application custodian stating each source and proposed event range, deletion-policy anchor or actual deadline, and collector/authority/storage scope; add one sentence explaining why preservation may precede root-cause knowledge. Then add a two-row comparison of P-A with P-B: decision each could change, known or unknown loss path, owner, present state and why the second availability check may proceed in parallel. Do not calculate a precise expiry merely by adding 24 hours to R9's timestamp or claim either source was collected. A score of 2 identifies the time-sensitive risk, privacy boundary and proposal-versus-collection distinction for both sources.

Extension: Include a specific question that the application custodian must answer about the deletion-policy anchor and a separate question about whether the oldest requested row is still retrievable. State the status of P-A and P-B using the exact words proposed, approved, collected or verified, without skipping intermediate states.

For full credit: A 24-hour rule is a duration, not a confirmed deletion instant; P-A's potential urgency does not authorize a bulk export or establish that a hold took effect.

MIN2 supplementary task (after the required artifact):

Additional task: Alongside the P-A and P-B proposed requests, identify the specific columns necessary for the next decision and a category of source field that should not automatically be distributed to the technical reviewer. Distinguish retention of a permitted source object from permission to distribute every column. Preserve the existing R15 unknown policy anchor and proposal-versus-collection states.

For full credit: The request names the business purpose, source selection, custodians, approving role and restricted reviewer scope. No completed export or broad personal-data permission is invented.

HOLD2 supplementary task (after the required artifact): Keep the original three-row P-A request and two-row P-A/P-B comparison. Add to the existing custodian-question field the exact source-hold receipt requirements: source namespace, approved booking/object IDs, event interval/time semantics, accountable authority, custodian, reported activation, review/expiry terms, observed effect and an explicit limit. State that a missing oldest requested row requires investigation rather than a fabricated expiry. Do not change P-A or P-B from proposed merely because HOLD2 contains a positive artificial run.

For full credit: The request remains a proposed authorized next step at 09:20, the R15 policy anchor remains unknown, the P-B retention rule remains unspecified, and neither a source hold nor a copy is claimed performed.

Worked answer: EX018 in the final appendix.

Lesson 09 — Correlation, quiet queries and reviewer handoff

Week 2, day 4. Work through the teaching and the worked checkpoint before attempting the assigned exercise(s). Compare your artifact with the final worked result only afterward.

2.9 Contained lab: correlate records without inventing an actor

The earlier labs examined when a row became visible and whether a copied file changed. A responder also needs to ask whether records from two systems refer to the same activity. An account label, a session identifier and a human identity are different things. If an application audit records desk-a but omits its session identifier, several identity sessions for that account could fit the same time. A nearby timestamp cannot choose one by itself. An exact identifier can narrow a candidate relationship, but it still does not identify the person who used the session or establish that the edit was authorized. NIST SP 800-92 provides high-level log-management background, and NIST SP 800-86 discusses investigation across source types. Neither older publication prescribes this book's matching code or claims that an account/time join proves attribution. The following method is an original beginner-level teaching fixture.

This is a separate optional Q1 replay, not a new R record or a correction to R13. In the canonical Round B evidence, R13 reports one session within its available range and lacks device details. Q1 deliberately supplies two overlapping sessions for a different synthetic comparison so a learner can see an ambiguous join. Never import Q1 into the 09:20 canonical case decision, cite it as a discovered Harbor Library production fact, or reinterpret R13 as false. Both tables below are invented, contain no patron data and use explicit UTC instants on the same fictional date. The session intervals are half-open: start <= event < end, so an event at the printed end time is outside that row.

Card Q1-A — isolated identity-session table. The snapshot is an exercise table, not an authenticated export. session_id values are synthetic labels; no password, token or device identifier is supplied.

Lesson table; scroll horizontally to read all columns →

Session Account Start UTC End UTC
S-A desk-a 09:00 09:15
S-B desk-a 09:02 09:12
S-C front-b 09:00 09:20

Card Q1-B — isolated application-edit table. A blank application session_id means the field is missing from this row; it is not a statement that no session existed. The booking labels X1–X4 are additional exercise-only identifiers and have no relation to B17–B20.

Lesson table; scroll horizontally to read all columns →

Edit Account label Event UTC Application session_id Booking
E-A desk-a 09:05 blank X1
E-B desk-a 09:07 S-B X2
E-C desk-a 09:18 blank X3
E-D desk-a 09:06 S-X X4

The first pass checks account label and interval. E-A falls inside both S-A and S-B, not S-C because its account differs. E-B falls inside both by time, but its explicit S-B key narrows the supplied candidate set to one. E-C has no account-and-interval match: the latest supplied desk-a session ends at 09:15. E-D falls inside both desk-a intervals by time, yet its explicit S-X key matches neither supplied ID. A responder should not silently discard the explicit key and report either S-A or S-B as the source. That would change the meaning of the evidence to make a join succeed.

The second pass asks whether the apparent matches deserve trust. The two tables might cover different collection windows, use clocks with different error, or have been filtered on different account identifiers. Q1 does not provide a query manifest, source-clock check, application schema showing that both session_id fields share one namespace, or a record of session rotation. E-C's zero matches therefore means no row matched in this supplied table under this rule, not that no session existed at 09:18. E-D's key mismatch may point to a missing identity row, a malformed application field or a wrong assumption about key namespaces. Its cause is unknown. Even E-B's one exact-key match does not prove who controlled S-B or why the edit occurred.

Reproduce the four classifications locally

The Python 3 standard-library program below keeps the invented rows in memory. It opens no file, makes no network request and alters no source. It validates that supplied session IDs are unique and that every interval has a start before its end; it then prints the candidates under the classroom rule. Use a scratch interpreter or reason through the rows on paper. If you change a fixture, label it a variant rather than rewriting Q1's original result.

from datetime import datetime, timedelta

DAY = "2026-10-11T"
SESSIONS = [
    ("S-A", "desk-a", "09:00", "09:15"),
    ("S-B", "desk-a", "09:02", "09:12"),
    ("S-C", "front-b", "09:00", "09:20"),
]
EDITS = [
    ("E-A", "desk-a", "09:05", ""),
    ("E-B", "desk-a", "09:07", "S-B"),
    ("E-C", "desk-a", "09:18", ""),
    ("E-D", "desk-a", "09:06", "S-X"),
]


def utc(clock):
    value = datetime.fromisoformat(DAY + clock + "+00:00")
    if value.utcoffset() != timedelta(0):
        raise ValueError("expected an explicit UTC instant")
    return value


seen = set()
parsed = []
for sid, account, start, end in SESSIONS:
    if sid in seen or utc(start) >= utc(end):
        raise ValueError("duplicate session ID or invalid interval")
    seen.add(sid)
    parsed.append((sid, account, utc(start), utc(end)))

for eid, account, event, explicit_sid in EDITS:
    moment = utc(event)
    window = [sid for sid, owner, start, end in parsed
              if owner == account and start <= moment < end]
    if explicit_sid:
        matches = [sid for sid in window if sid == explicit_sid]
        status = "KEY_AND_WINDOW_MATCH" if matches else "EXPLICIT_KEY_UNMATCHED"
    elif len(window) == 0:
        matches, status = [], "NO_WINDOW_MATCH"
    elif len(window) == 1:
        matches, status = window, "ONE_WINDOW_CANDIDATE"
    else:
        matches, status = window, "AMBIGUOUS_WINDOW_CANDIDATES"
    print(f"{eid}: {status} candidates={','.join(matches) or '-'}")

The method has an important asymmetry. An explicit key that fails to match is not treated like an absent key. The former is evidence of a mismatch under the assumed schema; the latter simply leaves account/time as a weaker possible relationship. ONE_WINDOW_CANDIDATE, if a variant ever produces it, means one row survived this table filter, not a measured probability that the row is correct. The program never emits a person's name or a compromise verdict because neither table supplies that evidence. It also does not check that the tables are complete or that the account labels were populated consistently; those limits belong in the learner's result note.

Practice task. Submit a four-row result table with event ID, status, candidate session IDs and one sentence stating what the result cannot establish. Then write a bounded custodian request for the missing query range, source-clock quality and shared session-key semantics. Identify the fictional incident lead as the person who must authorize any additional collection under the classroom role boundary; no learner may query a real identity or booking system. Finally, create a variant Q1-V: remove only E-B's explicit S-B key and predict which classification changes. Do not treat a changed result as a new canonical event.

Expected evidence: four classifications matching the printed program output, a limitation for each, a request that names the smallest useful source and fields, and the Q1-V prediction. Rubric (six points): one point for correctly identifying both E-A candidates; one for E-B's key-and-window match without human attribution; one for E-C's zero-match limit; one for preserving E-D's explicit-key mismatch; one for a proportionate request including clock, coverage and schema; and one for predicting that E-B becomes ambiguous when its key is removed. A 6/6 score verifies reasoning on this fixture, not the trustworthiness of a real log join. The exact output and model request appear in the final answer appendix after the Module 2 evidence-package audit result.

Q1-S — qualify identifiers and test sensitivity to clock uncertainty

Learning goal: Explain why equal key text is not enough to join two sources, calculate which session intervals could contain an uncertain event time, and keep input defects separate from an absence of matching evidence. Complete the four original Q1 cases first. This extension changes neither their inputs nor their four expected output lines.

Classification and safety: Q1-S is a separate, entirely invented fixture dated 2040-03-16. Its realms, account and correlation keys are harmless labels, not authentication material, Harbor Library observations or additions to R1–R16. The laboratory uses only Python's standard library and in-memory data. It performs no collection, login, session access, network request, source modification or cleanup operation.

Equal text versus an equal identifier

A namespace is the context in which an identifier has its meaning. A qualified key retains that context with the value. In this exercise, the qualified key is the pair (realm, key). Thus (realm-a, K7) and (realm-b, K7) are different identifiers even though their last component is identical. A real source contract might instead need an issuer, tenant, application, generation or another documented qualifier. Do not assume that this two-field classroom key is sufficient for every platform.

The following logical drawing represents filtering a supplied candidate table, not a network path or a successful authentication:

Application row: realm-a | K7 | desk-x
                         |
                         v
             Select the declared realm
                /                   \
       realm-a rows             realm-b rows
       K6, desk-x               K7, desk-x
       K7, desk-x               Excluded: wrong realm
             |
             v
       Select K7 and desk-x
             |
             v
       Test the time interval
             |
             v
       Candidate relationship only
       No person, permission or intent established

Text equivalent: Start with the application's declared realm, retain only rows in that realm, then compare the explicit key, account and time. The other realm's K7 does not become evidence about this row. When the realm is unknown, stop with an unresolved namespace rather than search every realm and select a convenient result. A supplied but nonmatching key remains nonmatching; it must not be replaced by a weaker account/time lookup.

Three concepts must also remain separate. A session correlation label relates records to a declared session. An interaction identifier relates records to one interaction, such as an individual request. A human attribution is a further evidential claim about who acted. The OWASP Logging Cheat Sheet discusses interaction identifiers as a way to preserve application-known relationships and distinguishes event time from logging time. It does not make this exercise's realm/key pair an authenticated identity.

Do not make a correlation exercise an excuse to copy live session secrets. OWASP's session logging guidance warns against recording raw session IDs and describes a protected correlation approach. For a real review, request only the approved non-secret correlation representation, its scope and its documented mapping. Q1-S already supplies safe invented labels; it teaches no token acquisition or session replay.

Time uncertainty changes a set of candidates, not the event's history

The original Q1 tests one stated event time against exact supplied session endpoints. Q1-S asks a different question: what changes when the event's comparable time is known only to lie in a closed range [low, high]? The range is an explicit assumption, not an ingestion delay, a measured probability interval or permission to use a later-released record at an earlier checkpoint.

For a session whose supplied interval is [start, end), define two tests:

Lesson table; scroll horizontally to read all columns →

Test Meaning under the stated model Comparison
Possible candidate At least one time in the event range lies inside the session interval low < end and high >= start
Covers all times Every time in the event range lies inside that session interval start <= low and high < end

The endpoint asymmetry is intentional. A session includes its start but excludes its end. An event range ending exactly at the session start still overlaps at that included instant. A range starting exactly at the session end does not overlap. A range that includes the exclusive end cannot pass the all-times test. These comparisons are derived from the declared intervals; they are not a NIST severity or confidence scale.

Work the mechanism: Suppose realm-a/K6 lasts from 09:00 to 09:15 exclusive and realm-a/K7 from 09:02 to 09:12 exclusive. An event with no key could have occurred between 09:11 and 09:13 inclusive. K6 contains the entire range. K7 contains only the part before 09:12. Both therefore remain possible, while only K6 covers all times.

UTC minute     00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15
realm-a/K6     [--------------------------------------------)
realm-a/K7           [-----------------------------)
event range                                     [-----]

Figure reading: Every tick is one minute after 09:00 UTC. [ and ] include an endpoint; ) excludes it. The event range intersects both session intervals but is contained only by K6. These are invented intervals, not collected timestamps. The table below gives the exact numeric endpoints independently of the drawing.

One all-times candidate is not a unique attribution: K7 remains possible if the event was in the earlier part of the range. The all-times test is robustness to this particular time assumption, not proof of source truth or a higher probability. Likewise, widening an assumed range until a desired session matches does not validate the range. A custodian would need to substantiate the clock relationship and bound. If no usable bound is supplied, Q1-S reports UNRESOLVED_TIME_BOUND instead of silently assuming zero error.

This model treats session endpoints as exact and varies only the event time. Uncertain session clocks, changes in clock offset, session-key reuse and incomplete exports require additional evidence or a different model. Do not import the example's two-minute event range into the canonical 09:20 decision.

Complete independent input and execution contract

The program uses integer minutes after midnight UTC on its artificial date: 09:00 is 540, 09:02 is 542, 09:07 is 547, 09:11 is 551, 09:12 is 552, 09:13 is 553 and 09:15 is 555. Integer endpoints make the arithmetic inspectable; the program is not a parser for production timestamps or sub-minute evidence.

Lesson table; scroll horizontally to read all columns →

Realm Key Account Start inclusive End exclusive
realm-a K6 desk-x 540 555
realm-a K7 desk-x 542 552
realm-b K7 desk-x 542 552

A repeated bare K7 across realms is permitted; a repeated qualified key is rejected. Repeated exported rows might represent duplicate delivery rather than two sessions, but this small program does not decide that question: preserve the packet and ask about row identity and export semantics instead of silently deduplicating conflicting records. The account and key comparisons are exact and case-sensitive; the model does not invent alias mappings or trim identifiers into equality.

key="" is the explicit missing-key marker for this fixture. A numeric zero is a type error, not a missing key. realm=None and event_range=None mean the required namespace or comparable time bound has not been supplied. all_times=() means no tested row covers every time, except when an UNRESOLVED status says the test was not performed; always read the status with the sets. An empty packet is unresolved. A nonempty packet with no matches still establishes only the result of filtering those supplied rows.

Run the complete listing below in a fresh Q1-S session, independent of the original Q1 program. It prints eleven lines. Predict them before consulting the Q1-S worked result.

from dataclasses import dataclass

@dataclass(frozen=True)
class Session:
    realm: str
    key: str
    account: str
    start: int
    end: int

# Minutes after midnight UTC on the invented date 2040-03-16.
# Keys are harmless classroom labels, not authentication tokens.
SESSIONS = (
    Session("realm-a", "K6", "desk-x", 540, 555),
    Session("realm-a", "K7", "desk-x", 542, 552),
    Session("realm-b", "K7", "desk-x", 542, 552),
)

def nonblank(value):
    return isinstance(value, str) and bool(value.strip())

def correlate(rows, realm, account, key, event_range):
    """Filter declared toy rows; do not authenticate sources or identify people."""
    if not isinstance(rows, (tuple, list)):
        raise ValueError("INVALID_PACKET")
    if not rows:
        return "UNRESOLVED_EMPTY_PACKET", (), ()
    seen = set()
    for row in rows:
        if not isinstance(row, Session):
            raise ValueError("INVALID_SESSION_ROW")
        if not all(nonblank(x) for x in (row.realm, row.key, row.account)):
            raise ValueError("INVALID_SESSION_IDENTIFIER")
        if (type(row.start) is not int or type(row.end) is not int
                or not 0 <= row.start < row.end <= 1440):
            raise ValueError("INVALID_SESSION_INTERVAL")
        identity = (row.realm, row.key)
        if identity in seen:
            raise ValueError("DUPLICATE_QUALIFIED_KEY")
        seen.add(identity)
    if not nonblank(account):
        raise ValueError("INVALID_ACCOUNT")
    if not isinstance(key, str) or (key and not nonblank(key)):
        raise ValueError("INVALID_EDIT_KEY")
    if realm is None:
        return "UNRESOLVED_NAMESPACE", (), ()
    if not nonblank(realm):
        raise ValueError("INVALID_NAMESPACE")
    if event_range is None:
        return "UNRESOLVED_TIME_BOUND", (), ()
    if (not isinstance(event_range, tuple) or len(event_range) != 2
            or any(type(x) is not int for x in event_range)
            or not 0 <= event_range[0] <= event_range[1] < 1440):
        raise ValueError("INVALID_EVENT_RANGE")
    low, high = event_range
    possible, all_times = [], []
    for row in rows:
        if row.realm != realm or row.account != account:
            continue
        if key and key != row.key:
            continue  # Never discard a supplied key to force a match.
        label = row.realm + "/" + row.key
        if low < row.end and high >= row.start:
            possible.append(label)
            if row.start <= low and high < row.end:
                all_times.append(label)
    status = "CANDIDATES_IN_SUPPLIED_ROWS" if possible else "NO_MATCH_IN_SUPPLIED_ROWS"
    return status, tuple(sorted(possible)), tuple(sorted(all_times))

CASES = (
    ("QUALIFIED_KEY", SESSIONS, "realm-a", "K7", (547, 547)),
    ("KEY_ABSENT", SESSIONS, "realm-a", "", (547, 547)),
    ("NAMESPACE_UNKNOWN", SESSIONS, None, "K7", (547, 547)),
    ("AT_EXCLUSIVE_END", SESSIONS, "realm-a", "K7", (552, 552)),
    ("BOUNDARY_UNCERTAIN", SESSIONS, "realm-a", "", (551, 553)),
    ("BOUND_UNKNOWN", SESSIONS, "realm-a", "K7", None),
    ("OMITTED_K7", (SESSIONS[0],), "realm-a", "K7", (547, 547)),
    ("EMPTY_PACKET", (), "realm-a", "K7", (547, 547)),
    ("DUPLICATE_KEY", SESSIONS + (SESSIONS[1],), "realm-a", "K7", (547, 547)),
    ("FALSE_KEY", SESSIONS, "realm-a", 0, (547, 547)),
    ("REVERSED_RANGE", SESSIONS, "realm-a", "K7", (553, 551)),
)

for name, rows, realm, key, bounds in CASES:
    try:
        status, possible, all_times = correlate(rows, realm, "desk-x", key, bounds)
    except ValueError as exc:
        print(f"{name}: {exc}")
    else:
        print(f"{name}: {status} possible={','.join(possible) or '-'} "
              f"all_times={','.join(all_times) or '-'}")

The central decision model can be read without Python; an empty packet also stops with an unresolved result:

Validate the supplied rows and field types
        | invalid                         | valid
        v                                 v
Report input defect              Namespace and time bound supplied?
                                          | no                | yes
                                          v                   v
                                     Unresolved       Filter realm and account
                                                              |
                                                     Keep any explicit key
                                                              |
                                                  Compare the two time tests
                                                              |
                                              Report sets AND remaining limits

Text equivalent: Malformed input cannot support a match. Missing namespace or time information produces an unresolved result. Otherwise preserve the stated realm, account and any explicit key, compute possible and all-times sets, and explain their limits. No branch approves collection, selects a human actor or triggers containment. The implementation reports the first applicable problem rather than proving every untested field valid.

Practice, evidence and assessment

Keep the original Q1 six-point task unchanged. For this separate Q1-S practice, submit the eleven-line transcript and an eleven-row ledger with case | what changed | result | blocked conclusion | smallest repair or follow-up. Then solve four variations without changing the original packet: (A) set the key to K7 in the boundary-uncertain case; (B) use K7 with the exact range (542, 542); (C) use K7 with (552, 553); (D) reverse the input row order for the qualified-key case. For each, predict both candidate sets or the reason no test is possible.

Finally, write one bounded request identifying the source/realm mapping, permitted non-secret correlation fields, event and collection ranges, clock-bound evidence, relevant export omissions and approval owner. Do not request every account or supply a real token. Record which parts of the request are proposals rather than received evidence.

Supplemental rubric, six points: one point for the eleven reproduced lines; one for distinguishing qualified keys and refusing an unknown realm; one for correctly deriving both time tests and the four variations; one for distinguishing invalid input, unresolved prerequisites and no match in a nonempty packet; one for explaining why a unique all-times row does not eliminate a second possible row; and one for the minimal authorized request with explicit source and attribution limits. These points assess Q1-S only. They do not add a numbered EX identifier, change the course's existing 0–2 exercise rubrics or certify an operational investigation.

Synthesis: A useful correlation result states its identity namespace, selection, time assumptions and remaining alternatives. Type checks keep malformed inputs out of the calculation; they do not authenticate evidence. A possible set is not a probability distribution, and an all-times set is not a list of proven actors. Continue with the existing quiet-query laboratory to examine the separate question of source coverage.

2.10 Offline laboratory: when is a quiet-query claim defensible?

Learning purpose: Check evidence release, query scope, ingestion-coverage metadata and an unknown retention anchor without replaying the existing T1/T2 search or E1 hash laboratory.

Classification: Entirely synthetic classroom variant VQ2. This is not a new R-series observation, an amendment to R14, a real ingestion watermark or a production collection.

Environment: Python 3 standard library, in memory. No files, network operations, credentials or service changes.

Input interpretation and acceptance contract

The VQ2 scenarios ask a hypothetical custodian whether a claim of no additional matching edit is even conditionally defensible for the application-audit / desk-a / 09:00 ≤ event < 09:12 UTC scope.

The fixture includes an artificial returned teaching identifier, VQ-01. This is not R9, is not authenticated to R9, and must not be merged with canonical Harbor Library events. A hypothetical ingestion watermark claims coverage through a specified event-time boundary. Even a passing result remains conditional on the custodian's attestation that the source, metadata, clock, collection health and query actually mean what the manifest claims.

A watermark is a claim about ingest progression, not a custody receipt or producer-completeness proof. Treat a quiet-query decision as a sequence of separate checks:

Expected producer events and source health
    → transport / collector continuity
    → ingestion and indexing progress at the declared cutoff
    → query source + account + closed-open UTC time interval
    → actual returned identifiers and query execution receipt
    → narrowly worded inference, with a recheck trigger

Text alternative: The arrow chain shows six necessary inquiries, not six verified controls in the classroom fixture. The code below checks only declared syntax, time ordering, query scope and returned markers. It does not verify producer events, gaps during transport, a collector's health, trustworthy ingestion metadata, the execution receipt or an independently attested query. A passing toy result therefore remains CONDITIONAL_IF_ATTESTED, never proof that no change occurred. A duplicated identifier is not a second independently collected event.

Primary-source context, not certification of VQ2: NIST SP 800-61 Rev. 3 (final, April 2025) treats incident response in the context of cybersecurity risk management; NIST SP 800-92 (September 2006) concerns enterprise log-management practices; the multi-agency Best practices for event logging and threat detection (22 August 2024) covers collection, storage and analysis. Their broad guidance supports scrutinizing evidence coverage; the exact fixture fields, status labels, clock values and expected outputs below are original synthetic teaching conventions, not prescribed NIST/CISA algorithms.

Pre-run predictions:

Reproducible Python 3 code
RELEASE = {"R9": "09:20", "R14": "09:20", "R15": "09:20"}

def minute(clock):
    if (
        not isinstance(clock, str)
        or len(clock) != 5
        or clock[2] != ":"
        or not (clock[:2] + clock[3:]).isascii()
        or not (clock[:2] + clock[3:]).isdigit()
    ):
        raise ValueError("expected an HH:MM clock")
    hour, minute_value = int(clock[:2]), int(clock[3:])
    if not (0 <= hour < 24 and 0 <= minute_value < 60):
        raise ValueError("clock out of range")
    return 60 * hour + minute_value

def can_use(record, decision):
    return minute(RELEASE[record]) <= minute(decision)

print(
    "release:",
    "R9@09:05=" + ("YES" if can_use("R9", "09:05") else "NO"),
    "R9@09:20=" + ("YES" if can_use("R9", "09:20") else "NO"),
)

base = {
    "source": "application-audit",
    "account": "desk-a",
    "start": "09:00",
    "end": "09:12",
    "run": "09:20",
    "watermark": "09:12",
    "returned": ("VQ-01",),
}

variants = [
    ("CANONICAL_R14", None),
    ("EARLY_INGESTION", {**base, "run": "09:12", "watermark": "09:07"}),
    ("HYPOTHETICAL_MANIFEST", base),
    ("OTHER_ACCOUNT", {**base, "account": "helper-b"}),
    ("IMPOSSIBLE_WATERMARK", {**base, "run": "09:12", "watermark": "09:16"}),
]

def assess(manifest):
    if manifest is None:
        return "UNKNOWN_MANIFEST"
    if not isinstance(manifest, dict):
        return "INVALID_MANIFEST"
    required = ("source", "account", "start", "end", "run", "watermark", "returned")
    if any(key not in manifest for key in required):
        return "INCOMPLETE_MANIFEST"
    if not isinstance(manifest["source"], str) or not isinstance(manifest["account"], str):
        return "INVALID_SCOPE_FIELDS"
    returned = manifest["returned"]
    if not isinstance(returned, tuple):
        return "INVALID_RESULT_TYPE"
    if any(not isinstance(value, str) or not value for value in returned):
        return "INVALID_RESULT_VALUE"
    if len(returned) != len(set(returned)):
        return "DUPLICATE_RESULTS"
    try:
        run = minute(manifest["run"])
        watermark = minute(manifest["watermark"])
        start = minute(manifest["start"])
        end = minute(manifest["end"])
    except (KeyError, ValueError, TypeError):
        return "INVALID_CLOCK"
    if start >= end:
        return "INVALID_QUERY_INTERVAL"
    if watermark > run:
        return "INCONSISTENT_METADATA"
    if (
        manifest["source"] != "application-audit"
        or manifest["account"] != "desk-a"
        or manifest["start"] != "09:00"
        or manifest["end"] != "09:12"
    ):
        return "SCOPE_MISMATCH"
    if watermark < end:
        return "INGESTION_NOT_COVERED"
    if manifest["returned"] != ("VQ-01",):
        return "RESULT_NOT_AS_ASSUMED"
    return "CONDITIONAL_IF_ATTESTED"

for name, manifest in variants:
    print(f"{name}: {assess(manifest)}")

policy = {"period_hours": 24, "anchor": None}
print("R15_DEADLINE:", "UNKNOWN" if policy["anchor"] is None else "REQUIRES_VALIDATION")

assert assess(variants[2][1]) == "CONDITIONAL_IF_ATTESTED"
assert policy["anchor"] is None
print("BAD_CLOCK:", assess({**base, "run": "25:99"}))
assert assess({**base, "run": "25:99"}) == "INVALID_CLOCK"

# Additional adversarial teaching manifests. None changes canonical R1–R16.
negative_controls = [
    ("MISSING_SOURCE", {key: value for key, value in base.items() if key != "source"}, "INCOMPLETE_MANIFEST"),
    ("MISSING_RESULTS", {key: value for key, value in base.items() if key != "returned"}, "INCOMPLETE_MANIFEST"),
    ("NOT_A_MAPPING", "application-audit", "INVALID_MANIFEST"),
    ("BAD_RESULT_TYPE", {**base, "returned": ["VQ-01"]}, "INVALID_RESULT_TYPE"),
    ("BAD_RESULT_VALUE", {**base, "returned": ("VQ-01", None)}, "INVALID_RESULT_VALUE"),
    ("DUPLICATE_RESULT", {**base, "returned": ("VQ-01", "VQ-01")}, "DUPLICATE_RESULTS"),
    ("EXTRA_RESULT", {**base, "returned": ("VQ-01", "VQ-02")}, "RESULT_NOT_AS_ASSUMED"),
]
for name, manifest, expected in negative_controls:
    actual = assess(manifest)
    print(f"{name}: {actual}")
    assert actual == expected, (name, expected, actual)
Exact expected output — executed against the displayed fixture
release: R9@09:05=NO R9@09:20=YES
CANONICAL_R14: UNKNOWN_MANIFEST
EARLY_INGESTION: INGESTION_NOT_COVERED
HYPOTHETICAL_MANIFEST: CONDITIONAL_IF_ATTESTED
OTHER_ACCOUNT: SCOPE_MISMATCH
IMPOSSIBLE_WATERMARK: INCONSISTENT_METADATA
R15_DEADLINE: UNKNOWN
BAD_CLOCK: INVALID_CLOCK
MISSING_SOURCE: INCOMPLETE_MANIFEST
MISSING_RESULTS: INCOMPLETE_MANIFEST
NOT_A_MAPPING: INVALID_MANIFEST
BAD_RESULT_TYPE: INVALID_RESULT_TYPE
BAD_RESULT_VALUE: INVALID_RESULT_VALUE
DUPLICATE_RESULT: DUPLICATE_RESULTS
EXTRA_RESULT: RESULT_NOT_AS_ASSUMED

The first eight output lines are unchanged from the original VQ2 transcript; the following seven are the new adversarial results. The complete displayed Python listing was validated as one program on a synthetic, in-memory fixture with Python 3.13.5. This is a local execution check, not independent expert review, an operational ingestion test or verification of the whole manuscript.

Annotated results and negative controls

Adversarial input diagnosis (same artificial query contract):

Lesson table; scroll horizontally to read all columns →

Control Reason for refusal Appropriate analyst response
MISSING_SOURCE, MISSING_RESULTS A mandatory query scope or result field is absent; the former listing raised KeyError instead of giving the learner a bounded outcome. Request the complete original manifest rather than assuming a zero-hit search.
NOT_A_MAPPING Free text is not a structured manifest. Identify the missing document and ask for its custodian.
BAD_RESULT_TYPE A list was supplied where this fixture requires a tuple; real log APIs can legitimately use other formats. Validate the source's actual schema; do not generalize the toy tuple rule to production.
BAD_RESULT_VALUE The returned marker tuple contains a non-string; this fixture cannot interpret it as a record identifier. Request a typed, documented result and execution receipt.
DUPLICATE_RESULT A repeated marker is not independent corroboration. Preserve the original rows and investigate duplication in the collection/query path.
EXTRA_RESULT VQ-02 violates this fixture's assumption that only VQ-01 is returned. Reopen the previously bounded claim and validate the newly reported row; do not call it a real incident event.

Counterexample and decision boundary: Two scenarios can print the same single marker even though one collector silently missed a producer event and the other retained every producer event. The toy program cannot distinguish them because the manifest does not carry independently verified production, transport and indexing receipts. Formally, let S mean "query scope matches" and W mean "watermark covers the end". S ∧ W is necessary under this lab's assumption but insufficient to prove event completeness. A custodian must separately attest source health, transport continuity, indexing and execution, and disclose remaining gaps. Even such an attestation supports only a scoped observation, not universal absence or a named actor. This is an original illustrative inference, not a numerical probability estimate.

Lab limitations: The minute helper handles only single-day HH:MM timestamps; it is not a timezone or multi-day event parser. It does not authenticate query metadata or establish clock correctness. VQ-01 is a synthetic result marker, not an application audit of R9. A conditional pass tests the logic of the stated envelope; it cannot prove producer completeness, an authorized export or a real incident.

Required submission: Provide the eight original baseline lines and the seven adversarial-control lines, a manifest-assessment table naming one uncertainty for each scenario, a two-sentence explanation of why the 09:05 decision cannot cite R9, and a preservation request that leaves R15's exact deletion deadline unknown. Add a proposed falsifier: identify the metadata or new qualifying row that would invalidate your current quiet-query conclusion. A syntactically plausible but out-of-range clock is not evidence of ingestion coverage.

Pass criteria: Exact reproducibility of all 15 output lines, including missing-field refusals without exceptions; no release-boundary violation; no unsupported retention arithmetic; clear distinction between a declared watermark and authenticated completeness; no merger of VQ2 into R1–R16.

2.11 Contrastive 09:20 decision example

Incorrect handoff:

"R9 proves the employee attacked B19 at 09:03. R14 proves no further edits happened, and R15 means we have exactly until 09:03 tomorrow to preserve the logs. The records are hashed, so we can declare the account compromised and close collection."

The sentence makes several unsupported jumps. R9 is an application-related booking record under an account label, not verified human attribution. R14 has unspecified source/query bounds and does not prove a universal quiet period. R15 does not define its retention anchor. No completed export or digest check for the underlying canonical records is supplied. Even a genuine matching digest would not settle authorization or root cause. The analyst also confuses account-compromise confidence with preservation urgency.

Correct bounded assessment:

"At the simulated 09:20 review, R9 reports a B19 change at 09:03 UTC; R10's supplied training plan names B17/B18 but not B19, and R12 records that the staff member does not recall B19. These observations warrant an authorized training-scope and service-impact check without identifying the human operator. R14 describes a quiet result only within its unspecified query range; R15 reports a 24-hour deletion policy whose anchor remains unknown. The incident lead should seek a bounded availability and preservation decision with the application custodian and review the matter by 09:35 UTC, or sooner on a verified new unexplained edit. No production copy, restriction or completed identity investigation is established."

The better version does not avoid making a decision. It makes a specific, reviewable next decision without asserting evidence that is not present.

2.12 Three-lane handoff for independent review

A handoff is complete when another analyst can trace important claims to sources, identify why an apparently stronger conclusion was rejected, and continue through an authorized next step. Include an exact checkpoint, the original record IDs, owner of the next request, decision status and revisit condition.

A compact handoff has three lanes:

Evidence lane: "R9 reports B19 edited; R10 omits B19 from the supplied plan; R11 records a second patron report; R13 lacks device/location detail." These are statements about the supplied records.

Assessment lane: "Training remains plausible for B17/B18; B19 authorization and human attribution remain unresolved; overall patron impact is not measured." These are interpretations with limits.

Action lane: "Propose P-A; request custodian confirmation of the retention anchor and actual source coverage; ask the service owner to authorize a bounded impact comparison; review at 09:35 or on a verified fresh edit." These are plans, not completed operations.

A receiver should be able to challenge the assessment without questioning whether an unseen action occurred. The handoff should say "requested" when requested, "approved" only when approval is documented, and "collected" only when a collection receipt exists. This discipline also prevents an exercise-only query manifest from being misreported as an operational source observation.

The next chapter may consider containment and recovery decisions, but Module 2 cannot pre-authorize those changes or introduce Round C evidence. Its immediate result is a bounded, preservation-aware decision ready for review.

2.13 Handoff and common errors

A useful handoff lets a second analyst reconstruct the decision without guessing: case ID; current time and timezone; affected service; observed facts with record IDs; live hypotheses; confidence and limits; known service impact; actions completed under which authority; evidence locations; next question; decision owner; review deadline. It should also separate the source's event time from the time the analyst learned of the event.

Common errors are copying only an alert title, silently converting timezones, treating a staff recollection as proof, relying on a log summary that omits key fields or query boundaries, hashing a file without recording which bytes were hashed, or using a high severity label without naming harm and urgency. A careful analyst can be concise: cite the observation, state the limit and identify the next decision. T1 shows why “I discovered R9 at 09:20” does not mean “R9 occurred at 09:20”; conversely, the 09:03 event time does not mean R9 was available for a 09:05 decision. T2 adds a second trap: repeating an event-time query after delayed ingestion can reveal another row without changing the event's occurrence time. The E1 package audit keeps byte integrity, source provenance and authority separate. Q1 adds a cross-source trap: an account/time overlap is a candidate relationship, while a missing or unmatched session key cannot be repaired by confidently naming a person.

Worked checkpoint

Two records may share a booking key and fall inside a declared time window while still leaving the human actor unknown. A quiet query is bounded by source, filter, ingestion cutoff and coverage; without those fields it cannot establish absence.

Practice — Lesson 09

EX022 — Handoff

Objective: allow a second analyst to continue without guessing. Inputs: the Round B snapshot R1–R16 and the fictional approval path. Output: a five-sentence handoff at 09:20 UTC that includes case ID and service, at least two evidence IDs, a live hypothesis and alternative, one uncertainty, an authorized next action, the custodian or owner, and the 09:35 review or an observable earlier trigger. A score of 2 distinguishes a request or proposal from a completed action.

Extension: Preserve the existing five-sentence maximum. Require three distinct verbal states in those sentences: a direct record observation, an assessment with its limitation, and a next action stated as proposed unless actual approval/execution is supplied. Do not imply that an approved classroom exercise is a completed production collection.

For full credit: The handoff names R9, R10 or R11 as appropriate, identifies unresolved B19 authorization, keeps R15's deadline uncertain and states the 09:35 review or earlier verified trigger.

HOLD2 supplementary task (after the required artifact): Preserve the five-sentence maximum at 09:20, with case ID, two evidence IDs, competing hypotheses, uncertainty, a proposed authorized next action, owner and 09:35 or earlier review. Within those sentences, say whether P-A/P-B are merely proposed and whether there is any confirmed source-hold effect. Do not insert HOLD2's simulated positive result or later R17–R24 evidence.

For full credit: The handoff gives a second analyst a clear request to the custodian about the R15 policy anchor and source-hold feasibility, without claiming a completed hold or an exact deletion instant.

Worked answer: EX022 in the final appendix.

EX024 — Escalation threshold

Objective: define a testable escalation rule. Inputs: the 09:20 case state and R15's retention notice. Output: two hypothetical observation/response pairs, each naming who verifies the new signal, why it raises harm or urgency, who decides on the appropriate response and when they reconvene. Containment and preservation require separate decisions. Do not claim either event happened. A score of 2 uses observable triggers rather than “if things get worse.”

HOLD2/TRI2 refinement: Keep exactly two hypothetical observation/response pairs. One concerns a future source-verified unexplained edit or owner-confirmed material booking mismatch, followed by human review of proportionate, separately authorized containment. The other concerns a future custodian-confirmed source-preservation failure, inability to retrieve the selected oldest item, or inability to clarify a material retention risk through the approved escalation route. Name the source, verifier, harm or loss rationale, decision owner and earlier review trigger for each. Neither trigger is observed in R1–R16.

For full credit: A source-hold failure trigger is distinct from confirmed malicious activity or proven deletion. The incident lead coordinates an authorized preservation alternative, not an automatic disruptive action.

Worked answer: EX024 in the final appendix.

Lesson 10 — Impact bounds, denominators and privacy

Week 2, day 5. Work through the teaching and the worked checkpoint before attempting the assigned exercise(s). Compare your artifact with the final worked result only afterward.

2.14 Define the thing you are trying to count

"How many were affected?" is not an answerable technical question until an analyst defines what an affected unit is.

A ticket is a report submitted to a service channel. A booking is an application business object. An edit event is a recorded operation. A patron is a person or account associated with service use. One person may open multiple tickets, one ticket may discuss several bookings, and one booking may have several audit records. These objects must not be treated as interchangeable.

In Harbor Library, R5 contains a patron report about B18; R11 is a second patron report about B19. R3, R4 and R9 describe booking changes. The supplied chapter does not include a complete current authoritative-state comparison or the population of all bookings. Therefore, at the 09:20 checkpoint, the learner can identify specific reported concerns and recorded changes, not an independently measured total of incorrect bookings or affected people.

Before any count, write the measurement question:

"Among the booking IDs included in an authorized, owner-defined comparison interval, how many have a current authoritative value that differs from the approved expected state?"

This is substantially narrower than:

"How many patrons were harmed by the incident?"

The second question requires additional relationships and harm criteria. A room assignment discrepancy could inconvenience a patron, but whether it caused actual harm, whether multiple patrons were associated with the same room, and whether the issue was corrected before use are all separate facts.

The general incident-response goal is to assess scope and consequences based on available evidence. NIST SP 800-61 Revision 3 places response analysis and decisions within cybersecurity risk management; the measurement contract below is original instructional material, not a prescribed NIST impact score.

2.15 Establish four levels of impact claim

It helps beginners to classify claims before attempting to calculate a total.

Lesson table; scroll horizontally to read all columns →

Classification Example claim What it establishes What it cannot establish alone
Reported concern A help-desk record says that a booking looked wrong A report was received That the authoritative state was incorrect or every symptom occurred as described
Recorded change Application audit says a room assignment changed The producer recorded a change Whether the change was approved, harmful, durable or visible to a patron
Verified state discrepancy An authorized comparison finds a current value differs from an owner-approved target A mismatch exists for the tested object and state versions, under the comparison's trust assumptions The size of the unexamined population, root cause or human actor
Verified service consequence A service owner validates a specific failed reservation outcome The documented scenario caused an unacceptable outcome within tested scope Whether all patrons experienced the same effect

These classifications can apply to the same booking; they are not mutually exclusive replacement labels. A report and an audit event can support investigating one booking, while a later state comparison adds a distinct, stronger claim. The analyst should preserve the original evidence and note which new source justified the upgrade.

A state discrepancy must also have a defensible reference target. "The current room is different from the old room" does not necessarily mean it is wrong: a legitimate staff edit could have been approved. The target needs an owner, version, applicable time and authority. If the target is missing, record the comparison as unverified, not automatically a mismatch.

2.16 The numerator and denominator have different evidence requirements

A count needs a numerator, the items meeting the definition, and a denominator, the items in the defined population against which the count could be interpreted.

Suppose a restricted comparison examines five selected booking IDs and finds two confirmed mismatches. A precise statement is:

"Two of the five selected IDs have a verified mismatch under the supplied comparison rules; the other three require their own status classification."

It would still be incorrect to claim:

"Forty percent of Harbor Library bookings were corrupted."

The selected five could have been chosen because they were suspicious, reported or otherwise convenient. Without evidence that the five represent the entire relevant population or a valid sampling design, their observed proportion cannot be generalized to every booking.

Selection may be case-driven rather than representative. An incident analyst often investigates the most urgent known objects first. That is appropriate for containment and service repair, but it creates a biased basis for estimating prevalence. Case selection and statistical sampling are different activities.

The analyst should separate:

Do not silently exclude unknown items from the report just to make the ratio look more favorable. Instead state, for example, "two mismatches, two matches and one unverified item within the five listed comparisons; no population-wide estimate available."

This approach is useful even when no percentage is calculated. Counts expressed with explicit scopes let the next analyst decide whether to broaden the inquiry or correct known business state immediately.

2.17 Deduplicating does not mean discarding evidence

A help-desk queue may contain repeated calls, email replies or follow-up tickets about one booking. Deduplication can prevent overcounting booking identifiers, but it must not destroy the fact that multiple reports were received.

Consider two fictional tickets referencing the same booking. For the question "How many distinct bookings have a reported concern?" the two tickets correspond to one booking identifier. For the question "How much help-desk demand was received?" they remain two separate contacts. For the question "How many people were affected?" neither answer is sufficient unless the distinct individuals and relevant outcome are reliably established.

Always preserve the original ticket IDs. A normalized table may group them by booking identifier, but the grouping rule must be explicit and reversible. If a booking ID is absent or misspelled, do not make an automatic identity assumption from a similar time or room number.

A dangerous shortcut is to treat multiple independent reports as multiple verified incidents or to count one booking once for each edit event. Another shortcut is to collapse all reports into one and then claim that only one person was affected. Each changes the unit without informing the reader.

The source/copy/interpretation separation explained in Chapter 02 also applies here. A normalized, deduplicated view is a derived analytical product. Keep the source pointers and transformations visible. NIST SP 800-92 discusses log-management processes and the handling of event records at an organizational level. It does not specify the fictional deduplication procedure in this section.

2.18 Missing information is an impact result, not a reason to guess

An absent owner-approved target, missing current-state row, incomplete query filter or unreliable source timestamp makes some impact claims unverified. This classification does not say the booking is healthy or broken.

A response team must distinguish:

  1. Negative observation: a defined, functioning, appropriately scoped test produced evidence supporting an absence or expected result within its bounds.
  2. Missing observation: the record or comparison needed for the test was not supplied or could not be trusted.
  3. Contradictory observation: a properly conducted comparison produced a result that fails the accepted target.

A query that returns no relevant audit rows while collection health is unknown belongs in the second category for any broad absence claim. A state comparison that explicitly shows a different accepted value may support the third category—but only if the target and current value have adequate provenance.

R14's quiet summary is a reminder: a bounded log query cannot certify that no changes occurred outside its query envelope. R15's 24-hour retention policy adds urgency to preserving the source needed for an impact comparison, but its exact deletion anchor remains unknown. These limits affect what can be measured as well as what can be investigated.

2.19 A defensible impact ledger

A useful impact ledger contains one row per selected business object, with separate columns for the sources that describe it. It should not put a free-form narrative in place of a test result.

Lesson table; scroll horizontally to read all columns →

Field Explanation
Booking ID Exact identifier within approved scope
Selection reason Audit edit, help-desk report or owner-defined test frame
Related evidence Pointers to source records and query or collection receipts
Approved reference Expected value, version, approval owner and applicable time
Current authoritative value Observed value, source, time and collection method
Comparison state Match, mismatch or unverified
Reported service effect Reported concern, verified consequence or not assessed
Uncertainty Missing records, source-health limits or conflicting interpretations
Next action and owner Bounded comparison, correction proposal or preservation request

An analyst should not add patron names or unrelated account details to a shared impact summary when booking identifiers and controlled references are sufficient. The service owner can evaluate business consequences and authorize further checks within the relevant privacy rules. NIST SP 800-86 provides source-aware guidance for incorporating forensic techniques into incident response; it does not provide a blanket authorization to collect personal data.

The impact ledger's selection reason is especially important. If every row was selected because someone complained, a ratio from those rows cannot estimate how common a problem is among people who did not complain. If the source is a partial application audit, the ledger cannot measure omitted write paths or disappeared records. State the missing part of the frame before recommending a new collection.

2.20 Contrastive worked example at the Round B decision

Plausible but incorrect assessment:

"R3, R4 and R9 show three booking edits, and R5 and R11 contain two patron reports. Five patrons were affected, so the incident has a confirmed five-user impact."

This is an invalid unit conversion. The three application changes are not five distinct people. The two help-desk reports are observations of reports, not independent verification of two incorrect authoritative states. R3/R4 may include approved training work. Nothing in the Round B case establishes a complete distinct-patron population or the final approved booking targets.

Correct bounded assessment:

"By the fictional 09:20 Round B review, Harbor Library has source records reporting changes to B17, B18 and B19, plus patron complaints involving B18 and B19. The approved scope and current authoritative state for those bookings have not been fully verified. The incident lead should request a bounded, service-owner-approved comparison of the relevant booking identifiers with the correct target state, preserving its source coverage and retention limitations. No total of affected patrons can yet be established from the supplied material."

This assessment makes a useful decision while refusing a fabricated count. It does not need to delay all protective action until every record is available; urgency, ongoing potential harm and evidence loss remain separate considerations.

2.21 Communicate a lower bound without claiming completeness

If an authorized comparison later verifies that two selected booking states are inconsistent with approved targets, the analyst can sometimes state at least two identified mismatches among the checked items. That is a lower bound for the known, explicitly identified objects under the test assumptions. It is not the total affected booking count unless the investigated frame is complete.

For a public message, even a correct internal count may be too specific if it exposes other patrons or could be misread as exhaustive. An internal service owner may need booking IDs, approved targets and corrective owners; a patron-facing note may only need to acknowledge a possible room-booking discrepancy and describe the owner-confirmed assistance channel. The communications role must not convert "we have identified two incorrect selected bookings" into "only two bookings were affected."

A review should ask which wording remains true if additional relevant source records arrive later. "Two mismatches were identified in the approved five-object comparison" remains true, assuming that comparison was valid. "Only two bookings were affected" can become false as soon as an additional affected object is found. The first is therefore a more resilient and auditable statement.

2.22 Failure modes and acceptance limits

Lesson table; scroll horizontally to read all columns →

Failure mode Misleading conclusion Safer interpretation
Repeated tickets for one booking Each ticket represents a new affected booking Keep ticket count and distinct booking count separate
A booking changed in an audit The change harmed a patron Validate authorization, approved expected state and service outcome
Five suspicious objects examined Results describe the full population State the selected frame and avoid prevalence extrapolation
Reference target unavailable No mismatch was found Comparison is unverified, not passed
Current record absent Booking was deleted Query, retention, source or export may be incomplete
Matching source and current values All service outcomes are correct Check applicable version, time, authorization and user-facing result
Two confirmed mismatches in a subset Exactly two bookings were affected overall At least two were identified within the declared subset, if trusted
No new complaint in the help desk No further harm occurred Reporting channels and coverage are incomplete observations

A beginner has met this section's objective when they can select the correct unit, preserve source and derived identifiers, distinguish confirmed mismatch from missing comparison evidence, and write a decision-ready conclusion without claiming a measured population they never examined.

2.23 Offline laboratory — IM2: Reports, distinct bookings and confirmed mismatches

Classification: Independently invented, offline-only teaching fixture. IM2 is not an additional Harbor Library R-series release, not an approved production comparison, and not P1/C1 from Chapter 03. Identifiers X01–X05 and X99 are fictitious classroom business objects; T01–T04 are fictitious report references.

Goal: Separate ticket count, distinct reported booking IDs, verified mismatches in an examined frame and unverified comparisons. Demonstrate how removing an approved reference changes the strength of a claim without changing the underlying hypothetical current value.

Environment: Python 3 standard library, in memory. No file operations, network calls, credentials, database writes or real identities.

Input assumptions: The dictionary TARGET supplies an artificial owner-designated comparison target; CURRENT supplies an artificial current-state view. These values are inputs invented for the exercise, not real owner approvals or authenticated exports. A value None means the target is missing. The report tuples are separate source observations; repeated booking references are preserved.

Before running, predict: (a) the number of reports, (b) the number of distinct booking references in those reports, (c) which examined IDs mismatch, and (d) what happens when the reference target for X04 is removed in a variant.

TARGET = {
    "X01": "Room A",
    "X02": "Room B",
    "X03": "Room C",
    "X04": "Room D",
    "X05": None,
}

CURRENT = {
    "X01": "Room A",
    "X02": "Room C",
    "X03": "Room C",
    "X04": "Room B",
    "X05": "Room A",
}

REPORTS = [
    ("T01", "X02"),
    ("T02", "X02"),
    ("T03", "X05"),
    ("T04", "X99"),
]

The report list is preserved as four contacts. The classifier below builds a union of IDs, then checks whether each one has both values needed for a comparison. Run the three code blocks together in order in one Python session.

def compare_one(booking_id, target, current):
    expected = target.get(booking_id)
    observed = current.get(booking_id)
    if expected is None or observed is None:
        return "UNVERIFIED"
    if expected == observed:
        return "MATCH"
    return "MISMATCH"

def classify(target, current, reports):
    ids = sorted(
        set(target) | set(current) |
        {booking_id for _, booking_id in reports}
    )

    counts = {
        "MATCH": 0,
        "MISMATCH": 0,
        "UNVERIFIED": 0,
    }

    results = {}

    for booking_id in ids:
        status = compare_one(booking_id, target, current)
        results[booking_id] = status
        counts[status] += 1

    distinct_report_ids = {
        booking_id for _, booking_id in reports
    }

    print(
        "reports:", len(reports),
        "unique_booking_refs:", len(distinct_report_ids)
    )

    for booking_id in ids:
        print(f"{booking_id}: {results[booking_id]}")

    print(
        "counts:",
        ",".join(f"{key}={value}" for key, value in counts.items())
    )

    return results

The negative control removes a reference from a copy of TARGET; it does not change CURRENT or edit a booking. Predict the change in claim strength before running this final block.

original = classify(TARGET, CURRENT, REPORTS)

# Negative control: remove the approved comparison
# value from a copy, without modifying the baseline.
variant = dict(TARGET)
variant.pop("X04")

negative_status = compare_one("X04", variant, CURRENT)

print("negative_X04_target_removed:", negative_status)

assert original["X04"] == "MISMATCH"
assert variant.get("X04") is None
assert compare_one(
    "X04", {**TARGET, "X04": CURRENT["X04"]}, CURRENT
) == "MATCH"

Compare your result with the IM2 worked transcript and interpretation in the final answer appendix. The silent equality assertion checks the same comparison rule used by the baseline and missing-target variant; an existing target does not always imply a mismatch.

2.24 Privacy-aware evidence selection and reproducible review views

A complete source and a useful analyst view are not the same object

An incident investigation often needs enough information to distinguish competing explanations. That does not mean every analyst needs every field from an application export. A raw system record may contain account labels, booking identifiers, timestamps, request identifiers and additional contact information. Some fields could help answer the immediate incident question; others may reveal personal information unrelated to it.

The existing Chapter 02 already insists on bounded collection and trustworthy source provenance. It explains that byte-identical copies do not prove source completeness and that a quiet query cannot establish absence outside its envelope. This expansion asks a distinct question: After an authorized source has been identified, which fields may a specific reviewing role receive for a specific question, and how can the transformed view remain reproducible without unnecessarily spreading unrelated information?

Three objects should be distinguished:

A review view is not a more authentic original just because it looks tidy. It is an analytical product. If it hides a relevant source identifier, the reviewer may lose the ability to verify what was selected. If it exposes every available patron contact field, the team may exceed its stated purpose. Both are quality failures.

NIST SP 800-122, Guide to Protecting the Confidentiality of Personally Identifiable Information (PII) provides primary guidance on identifying and protecting PII according to risk and context. It is focused on U.S. federal agencies and does not establish the privacy law or authorization rules governing the fictional Harbor Library. The field-level exercise below is original OpenCyber Academy instructional material.

Begin with a permitted decision question, not a list of available columns

Consider the question available at the 09:20 Round B review:

"Which recorded operation concerning B19 should the application custodian investigate, and which available account/time identifiers could help test the approved-training explanation?"

This question may require a booking identifier, operation type, timestamp, account label, source name and record ID. It does not automatically require a patron's home address, email, telephone number, newsletter membership or unrelated reservations.

The decision purpose comes first. Then the analyst and custodian identify the minimum relevant sources, permitted rows and columns, approval and whether any sensitive field is truly necessary. The need for personal information may change if a different authorized question is posed—for example, notifying a person about a verified service consequence. That later purpose requires its own authority and evidence.

A weak collection plan says, "Export every user detail in case it becomes useful." This gives future unknown questions control over today's data exposure. A stronger plan identifies a current question and the specific fields that could discriminate among current hypotheses, with a way to request more through an authorized owner if the original selection proves insufficient.

The correct goal is proportionate access without destroying legitimate investigative capability. It is not simply making every exported file as small as possible. An underspecified view can falsely suggest that two events are unrelated because the join identifier was removed. Conversely, a broad export may include sensitive data with no decision value.

At rapid level, the student should state the question, list each requested field, describe its purpose and identify a permitted alternative if that field cannot be shared.

Separate record selection from field projection

Two distinct filtering operations commonly occur when preparing evidence for review.

Row selection determines which events or business objects appear. A proposed Round B selection might concern B17–B19 over an approved interval, rather than every historical booking. The exact source and time filter must be documented. R15's deletion rule creates urgency, but does not specify a deadline anchor or authorize a broader export.

Field projection determines which properties of those selected rows appear in the working view. One reviewer may need event IDs, booking IDs, timestamps, operation names and account labels. A different role may need an aggregate service-status note rather than technical identifiers. Neither view is an original log.

Both transformations must be recorded. A view's reported row count is meaningless without the selection that produced it. A small number of rows may reflect a narrow scope, a data absence, a wrong filter or a broken collection path. Field projection cannot repair any of those problems.

The selection plan should name the producer, approved query interval, relevant account or booking filters, source clocks, collection/ingestion limitations, collector and reviewer roles. Chapter 02 already develops those concepts for R13/R14, VQ2 and T1/T2. This supplement adds a required second line: Which exact columns are retained or withheld for the receiving analyst, and which original source custodian can recheck an omitted field if needed?

A changed selection or projection produces a different analytical artifact and must be versioned, not silently substituted for the first output.

The minimum view must still answer the question

Removing irrelevant data is valuable only if the remaining view can support a defensible conclusion. Suppose an analyst wants to associate an application change with a reported booking symptom. If the view removes the booking identifier, the analyst may not be able to connect the records. A row with only an operation and time can be insufficient, especially when multiple requests occurred nearby.

A required correlation field is a field necessary to compare the selected records under the approved method. Its presence does not make a correlation valid by itself. Two systems may use similarly named IDs with different namespaces or semantics. The existing Q1 laboratory demonstrates that an overlapping time window and account label can leave more than one candidate session. A privacy-aware view must not erase that uncertainty or use masking as an excuse to guess which session was involved.

An application-account label such as desk-a can itself be sensitive operational context. It may be appropriate for a restricted technical reviewer but unnecessary for a patron-facing message. The fact that a field is not a full legal name does not make it safe for unlimited distribution.

A field checklist therefore needs two explanations: why retained and why not retained. For example, keep a stable source event identifier so another reviewer can retrieve the correct raw row through an authorized custodian. Keep an operation type to distinguish edit from view. Omit a contact address if the current question does not need it. If a field's necessity is unknown, pause the release decision and ask the approving role rather than selecting it automatically.

Pseudonymization is not the same as anonymity

Sometimes a team wants to replace a directly identifying value with a less revealing label. For example, a review table might use an internal case alias instead of a patron contact address, while a restricted custodian maintains the approved mapping. Such a transformation may reduce unnecessary exposure, but it does not establish that a person can no longer be identified.

A consistent alias may link a person's activities across multiple rows, even when the person's name is absent. Other available fields—time, booking pattern or account metadata—may allow association with a real individual when combined with additional sources. A pseudonym should therefore still be treated according to its remaining disclosure risks.

Nor is a cryptographic digest of a contact address necessarily an anonymous value. Predictable inputs may be guessable, a shared digest can link repeated activity, and any reversible mapping creates a reidentification route. It would be incorrect to say, "The email was hashed, so the export contains no sensitive data." A secure pseudonymization scheme, if needed, requires a separately approved design, key protection, access control and threat assessment, beyond this introductory exercise.

The MIN2 laboratory deliberately omits contact fields rather than generating pseudonyms. It checks that the intended reviewer receives only declared analysis columns and that the source retains its original teaching values unchanged. This is easier to audit and avoids suggesting a toy hashing operation can solve privacy risk.

Anonymity is a much stronger claim than "a visible name has been replaced." Nothing in this chapter's fixture establishes irreversible anonymity.

Keep the original available under an appropriately controlled procedure

A bounded review view should not overwrite or replace the original record set. An analyst may need to verify whether a transformation omitted a relevant field or merged two events incorrectly. If the original was deleted, that review may no longer be possible.

For a real authorized collection, the relevant custodian should follow organizational retention, access, preservation and evidence-handling rules. The preservation may require restricted access to fields that are not distributed to every analyst. Protecting the source and minimizing the working copy are complementary, not contradictory, when they are each justified by their purpose and authority.

This does not mean preserving all personal data indefinitely. The original collection itself must be authorized and proportionate, and retention obligations depend on the organization's rules and applicable requirements. The book does not provide a general legal preservation order or permission to copy unrelated patron data.

In Chapter 02, the E1 exercise already distinguishes a synthetic transcription from an authenticated original and shows that matching SHA-256 values support limited byte consistency. A new working view should have a transformation manifest and source pointer; it cannot inherit authenticity merely because its source file had a digest check.

An effective manifest records: source identity, query selection, collection status, reviewer access scope, fields retained, fields deliberately omitted, transformation version, creator and creation time if actually performed, and the known limits. A planned transformation with no created output must remain planned, with no fabricated timestamp or digest.

Privacy and investigative value can fail in opposite directions

A review view can fail through excess or insufficiency.

An excess-field failure occurs when the output includes contact information outside the approved purpose. The analyst may still be able to investigate, but the disclosure is broader than necessary. A missing-key failure occurs when the output omits the booking or event identifier needed for the intended comparison. This may appear more protective at first glance, but it can make the authorized review unreliable.

These failures do not cancel each other. One cannot justify unnecessary contact data simply because the view contains other useful columns. One cannot call an unusable view successful just because it exposes fewer fields.

A third failure occurs when the row filter expands beyond the approved objects. A view may have an entirely appropriate set of columns but include records for an unrelated booking. Field-level restrictions do not automatically justify overbroad row selection.

The negative controls in MIN2 test all three conditions separately: removing the required booking key, adding a contact field and including an unapproved booking ID. The program rejects each variant instead of quietly producing a wider or less useful view.

This is an original data-handling acceptance contract. It does not attempt to determine every possible category of personally identifiable information or replace a full privacy impact assessment.

Contrastive worked example: "we have only the facts we need"

Incorrect hypothetical assessment:

"For R9, export the full patron profile, all contact details and all newsletter fields. The analyst might need them later. After copying them, delete the original and keep only a masked table; the mask proves the evidence is anonymous and complete."

The first step lacks a specific decision purpose for unrelated contact data. The second risks destroying the source context and provenance. The third converts a superficial masking operation into unsupported claims of anonymity and source completeness. None of those steps is authorized by R9 or the Round B release.

Defensible decision proposal:

"At 09:20, R9 reports a B19 booking edit, while R10–R13 provide limited training, patron-report, recollection and session context. The analyst requests that the application custodian confirm the approved source and select only the records and fields needed to investigate the B19 operation within a bounded interval. A technical review view may retain stable event/booking IDs, event time, operation and appropriate account context, while unnecessary patron contact details remain outside that view. The source and any transformation are preserved only according to the approved collection and access rules. No export or identity finding is claimed completed."

The proposal answers a specific question, names the custodian and leaves missing proof visible. It also prevents the opposite error: removing every identifier and then attributing events through timestamps alone.

Release boundaries still govern restricted views

A projection cannot make later evidence available earlier. At the 09:05 Round A checkpoint, R9–R16 do not exist in the learner's released set, even though R9 reports an event at 09:03. A review view derived from R9 is therefore not available for the Round A decision.

At the 09:20 Round B checkpoint, R1–R16 are available as supplied synthetic cards. The learner can design an authorized review request but must not claim that a custodian delivered unprovided raw fields. R13 lacks device and location detail; privacy minimization does not entitle the learner to fill those fields with guesses. R14's query scope is incomplete; a nicely projected table cannot repair that coverage limitation.

R15 adds a separate urgency to determine source retention, but its 24-hour deletion rule lacks a documented anchor. No projection can compute a defensible exact deadline without that missing policy detail.

The MIN2 rows are exercise-only symbols. They are not the same as the original R-series booking values, and they cannot be used to assert how many Harbor Library patrons were affected. The IM2 exercise addresses a separate counting problem, with its own X-series data and limits; MIN2 addresses how a narrowly scoped review view should be constructed and rejected when its field or object policy is violated.

Different reviewers should not automatically receive identical views

Operational teams contain distinct roles. A technical analyst investigating whether a booking edit and session event might be related may need an account label and source identifier. A service owner deciding whether booking state should be corrected may need booking IDs and approved targets. A communications contact preparing a patron-facing update may need a description of the service consequence, not an internal account name or raw contact list.

A single all-purpose export can encourage unnecessary forwarding between those roles. The better practice is to specify the receiving role and purpose before preparing the view, with explicit escalation when a reviewer needs more.

Do not treat role restriction as a replacement for source integrity. The service owner still needs valid authoritative booking data. The privacy contact may need to assess an allegation through a different approved process. A communications role's lack of raw data must not be "fixed" by inventing missing facts.

The fictional case does not define an organizational data-classification scheme or actual permissions. A course learner can design candidate views and approval questions while leaving operational implementation pending. It would be misleading to name a specific privacy law or notification threshold without the jurisdictional and organizational facts required.

Source identifiers and account labels retain uncertainty

A source event identifier lets a reviewer ask a custodian for the precise original row. It does not by itself authenticate the producer or prove that the data were complete. A booking ID connects a review row with a business object under that source's semantics; it may not be globally unique across all environments or time periods.

Similarly, an account label says what the source attributed to an account or session context. It does not identify the human operator. In Harbor Library, desk-a is an account reference. The absence of device/location detail in R13 remains an unresolved question even if a derived view perfectly retains the account field.

These limits are important because a carefully minimized view may look authoritative. Exact row IDs, tidy column names and consistent aliases can make a table appear complete. A disciplined reviewer asks which producer emitted each field, whether the join identifiers are actually comparable, whether the source query covered the intended interval and what was omitted.

MIN2's actor_account column is therefore a fictional correlation candidate, not a human attribution result. Its inclusion is allowed only within the exercise's explicitly defined technical-review policy, not as permission to publish account details widely.

Record transformation without pretending the source changed

A derived review view is not a modified original source; it is a new analytical artifact. A transformation log should allow another authorized reviewer to repeat the same row selection and field projection if the original is still available under the applicable retention rules.

For a small classroom review, record:

  1. The permitted question and receiving role.
  2. The approved booking/object scope.
  3. The source record count and declared producer.
  4. The exact fields retained and omitted.
  5. Any normalization of timestamps or identifiers.
  6. Whether the required correlation fields remain available.
  7. The output row count and any errors.
  8. The transformation code/version.
  9. The result's limitations and who may resolve missing fields.
  10. The fact that no real collection or release occurred.

A report saying only "personal details removed" omits too much. It does not identify which personal details, which source rows, which version or whether the transformed view can answer its intended question. A report saying "privacy protected" is even less testable.

The test should include negative cases. If the policy requires a booking ID and it is removed, the view must fail the task's acceptance condition. If a contact email is added, it must fail the field-minimization gate. If an unrelated booking is selected, it must fail the row-scope gate. Rejecting an unsafe requested view before producing it is a legitimate successful outcome for this exercise.

An authorized extension is not automatic scope creep

Sometimes the initial view is genuinely insufficient. Perhaps an authorized investigator cannot distinguish two records without a session identifier held in a different source. The right response is not to silently broaden every analyst's access. The investigator should document which decision depends on the missing field, why the current view is insufficient, and which custodian and approver can evaluate a targeted extension.

Such a request may be granted, denied or returned for clarification. Any newly approved selection is a distinct transformation version with its own source and limits. An extension does not imply that the old view was dishonest or that the newly supplied data retroactively existed at an earlier checkpoint.

A useful stopping rule is: if the evidence required for a claim is not inside the authorized current view, stop that claim and request additional permission or mark it unresolved. Do not use a field guessed from context to fill a missing column. If the current question can already be answered under its limits, do not collect unrelated information merely to make the record larger.

A practical incident process balances confidentiality, integrity and availability of evidence. Preserving too little can prevent a needed investigation; distributing too much can create another exposure. A well-specified purpose and validation rule keeps both risks visible.

Section synthesis: minimize the view without minimizing the truth

Evidence work needs two kinds of discipline at once. The source and its provenance must remain trustworthy and correctly scoped, while each review copy should contain only the information required for that receiver's approved question. Neither a complete original nor a minimal view automatically produces a correct causal conclusion.

For Harbor Library at 09:20, the right result is still a bounded investigation proposal, not a completed source export, a confirmed human actor or a population-wide impact count. MIN2 shows how a review can fail because it contains an unnecessary contact field, lacks a join identifier or reaches an unrelated booking.

The transferable beginner skill is to explain what was included, what was omitted, why both choices matter, and what the reviewer still cannot establish.

MIN2 Offline Laboratory — Select a Review View Without Losing Correlation

Status: Entirely synthetic and isolated. MIN2 is not R1–R16, not a real patron export and not the earlier E1, Q1, VQ2 or IM2 fixture.

Goal: Produce only the approved minimal technical-review fields for an approved synthetic booking selection; reject a missing join field, unnecessary contact field and expanded object scope. Preserve the original artificial source rows unchanged.

Environment: Python 3 standard library, memory only; no files, external requests, accounts or production data.

Input meaning: Q11 is the only approved booking ID in this exercise. The invented patron_email values use reserved example.test addresses. The actor_account field is a fictitious technical label, not a human identity. A READY label does not prove the source is authentic, the selection complete or the query authorized in a real setting.

Executable Python code
EVENTS = [
    {
        "event_id": "SYN-A", "booking_id": "Q11",
        "event_time": "09:03", "operation": "EDIT",
        "actor_account": "staff-x",
        "patron_email": "ava@example.test",
    },
    {
        "event_id": "SYN-B", "booking_id": "Q11",
        "event_time": "09:06", "operation": "VIEW",
        "actor_account": "staff-x",
        "patron_email": "ava@example.test",
    },
    {
        "event_id": "SYN-C", "booking_id": "Q12",
        "event_time": "09:08", "operation": "CANCEL",
        "actor_account": "staff-y",
        "patron_email": "bea@example.test",
    },
]

SOURCE_SNAPSHOT = [dict(row) for row in EVENTS]

ALLOWED = {
    "event_id", "booking_id", "event_time",
    "operation", "actor_account",
}
SCOPE = {"Q11"}
REQUIRED = {"event_id", "booking_id", "event_time", "operation"}

def review(label, fields, bookings):
    if not bookings:
        print(f"{label}: REJECT_EMPTY_SELECTION selected=0")
        return
    if not bookings.issubset(SCOPE):
        print(f"{label}: REJECT_SCOPE selected=0")
        return

    extras = fields - ALLOWED
    if extras:
        print(f"{label}: REJECT_EXTRA_FIELD {','.join(sorted(extras))}")
        return

    missing = REQUIRED - fields
    if missing:
        print(f"{label}: REJECT_MISSING_KEY {','.join(sorted(missing))}")
        return

    subset = [
        {key: row[key] for key in sorted(fields)}
        for row in EVENTS
        if row["booking_id"] in bookings
    ]
    if not subset:
        print(f"{label}: REJECT_NO_SOURCE_ROWS selected=0")
        return
    print(
        f"{label}: READY rows={len(subset)} "
        f"fields={','.join(sorted(fields))}"
    )
    print(
        f"{label}: bookings="
        f"{','.join(sorted({x['booking_id'] for x in subset}))}"
    )
    return subset

review("MINIMAL", ALLOWED, {"Q11"})
review("DROP_BOOKING", ALLOWED - {"booking_id"}, {"Q11"})
review("ADD_EMAIL", ALLOWED | {"patron_email"}, {"Q11"})
review("EXPAND_SCOPE", ALLOWED, {"Q11", "Q12"})

print(
    "SOURCE_UNCHANGED:", len(EVENTS),
    "rows; sensitive field retained in synthetic source only"
)
assert len(EVENTS) == 3 and "patron_email" in EVENTS[0]
assert EVENTS == SOURCE_SNAPSHOT
view = review("VIEW_CHECK", ALLOWED, {"Q11"})
assert view is not None and len(view) == 2
assert all("patron_email" not in row for row in view)
review("EMPTY_REQUEST", ALLOWED, set())
saved_events = EVENTS
try:
    EVENTS = [dict(saved_events[2])]  # only Q12 in this variant
    review("NO_Q11_ROWS", ALLOWED, {"Q11"})
finally:
    EVENTS = saved_events
assert EVENTS == SOURCE_SNAPSHOT

Predict the six baseline and four additional output lines; explain the three original rejected variants and the two new empty-selection controls before checking the MIN2 worked result in the final appendix. The fixture contains no real personal data. The returned projected view is for the approved fictional review role only; READY does not establish source authenticity, complete collection or real custodian permission.

2.24a Evidence availability is not the same as event time

Learning outcome: Before making a triage or preservation decision, distinguish when a fact allegedly occurred, when the producer recorded it, when a custodian acquired it, and when the responder could actually use it. The same record can have a very early event time and still be unavailable at a prior decision. This is not a pedantic issue: the ability to defend a proportionate decision depends on knowing which facts the decision maker could have seen.

At the first 09:05 checkpoint, R1–R8 are supplied. R9 describes an edit at 09:03 but is withheld until Round B. The analyst cannot treat that event as observed at 09:05 simply because its clock precedes the review time. At 09:20, R1–R16 make the B19 question visible; R17's later approved-task detail has not yet been released. At 09:37, the R1–R24 release supports a containment proposal, not a completed change. P1/C1 and V1 are later exercise cards and cannot justify that proposal.

When building an evidence ledger, record these separate attributes: source event time, ingestion/indexing time where known, case acquisition or receipt, learner/facilitator release, role authorized to view, source-query coverage, and status of the asserted observation. A blank value remains unknown rather than a value silently supplied from another card. A negative query needs declared source and clock coverage; “no result” is not evidence that nothing occurred outside the selected window or after the ingestion cutoff.

Contrastive example: A participant prepares a 09:05 note saying "R9 proves an unauthorized B19 edit." This fails twice: R9 was not released and it does not identify an unauthorized human. A valid note says "R1–R8 show the named booking events and the possibility of scheduled training; ask the service owner whether the work was permitted and set a bounded review." When R9 becomes available, the participant versions the note and adds B19 as a new reported edit, preserving the earlier snapshot. A second false shortcut uses R17 to pronounce at 09:20 that all authorized tasks are known. The later task list cannot cure the earlier uncertainty retroactively.

Why privacy matters: An authorized, source-complete producer copy and a minimized analyst view are different objects. A non-privileged learner can receive a pseudonymized booking/state projection without obtaining raw patron contacts or every identity field. A projection must retain its query, transformation, source object identifier, date and limitations. If the source was never collected, a tidy projection with plausible rows is no substitute. The analysis should never reconstruct withheld identity data from guesses to make its graph feel complete.

NIST SP 800-61 Rev. 3 provides the current risk-management setting for incident response; NIST SP 800-86, published in 2006, supplies foundational but dated discussion of forensic source handling. The release schedule below is an original OpenCyber Academy teaching rule, not a NIST timetabling requirement.

R2X offline lab — prove the decision's evidence horizon

Use Python 3 offline, with no imports, files or network. The values are pedagogical release levels for selected case cards, not actual service event times. The two D stages are intentionally separated: D1 is the branch-only checkpoint, D2 follows P1/C1 release. V1 remains a later independent card.

FIRST = {"R1": 0, "R9": 1, "R17": 2, "P1": 4}
LEVEL = {"A": 0, "B": 1, "C": 2, "D1": 3, "D2": 4}

def release(stage, card):
    return "RELEASED" if LEVEL[stage] >= FIRST[card] else "WITHHELD"

cases = [
    ("EVENT_BEFORE_RELEASE", "A", "R9"),
    ("ROUND_B_R9", "B", "R9"),
    ("PREMATURE_R17", "B", "R17"),
    ("PREMATURE_P1", "C", "P1"),
    ("LATER_P1", "D2", "P1"),
]
for label, stage, card in cases:
    print(label, release(stage, card))

Literal tested output:

EVENT_BEFORE_RELEASE WITHHELD
ROUND_B_R9 RELEASED
PREMATURE_R17 WITHHELD
PREMATURE_P1 WITHHELD
LATER_P1 RELEASED

Adversarial challenge: An analyst substitutes R9's event time for its Round B release stage and incorrectly calls EVENT_BEFORE_RELEASE a pass. This is a semantic failure, not a clock-parsing bug. A second challenge promotes P1's 09:40 owner-target time into a 09:37 decision; the classifier correctly says WITHHELD. The code does not authenticate a source, approve collection or determine whether a reported edit was malicious.

Reproducible practice: Produce the five printed lines, a two-snapshot note at 09:05 and 09:20 and a one-paragraph comparison of source acquisition, learner release and actual query coverage. Award full credit when the learner preserves the earlier decision version and identifies a bounded owner-approved next source request without copying personal data. This laboratory creates no new EX identifier, event, evidence receipt or booking change.

Synthesis: More data at a later checkpoint can improve the next decision. It cannot establish that an earlier decision was supported by information the responder did not yet have.

Worked checkpoint

When two supplied reports describe the same booking, count one distinct booking and retain both pointers. If the authoritative state is unavailable, place the booking in an unknown bucket rather than the confirmed-correct or confirmed-wrong count.

Practice — Lesson 10

EX023 — Impact check

Objective: size possible service harm without exporting unrelated patron data. Inputs: R9, R11, R14–R16 and the service map. Output: a short query request naming approver, custodian, booking identifiers or short event range, necessary fields, expected result table and privacy/access limit. The table should distinguish an audit edit, an owner-approved state and an unresolved mismatch. A score of 2 does not claim a completed count or that R14 covers the whole period.

Extension: Define a positive result and a negative result for the proposed booking comparison. A positive result must distinguish recorded edits from the current authoritative state; a negative result must explain how a mismatch or missing state affects the confidence of the claim. Record which patron fields remain outside scope.

For full credit: The learner cannot supply a measured affected-patron count from R9/R11 alone or treat R14 as complete coverage.

Additional measurement contract for EX023: State the unit (distinct booking ID), the selected comparison frame and why it was selected, the owner-approved target/current-state fields, and separate reported concern, recorded edit, verified mismatch and unverified comparison. A case-selected numerator is not a population prevalence estimate. As optional offline practice, use IM2 to show how repeated tickets and missing targets remain visible; never import X01–X99 into the R-series case.

MIN2 supplementary task (after the required artifact):

Additional task: Add a field-by-field justification for a proposed restricted booking-impact comparison. Distinguish what the analyst needs to classify a selected booking discrepancy from any contact details needed for separate patron communication. The latter require a different permitted purpose and must not be silently included in the technical comparison.

For full credit: The learner still defines the measurement unit and selected frame as taught in IM2, keeps unverified rows visible and does not infer affected people from ticket count or claim a completed comparison from R1–R16.


Worked answer: EX023 in the final appendix.

EX025 — Correct a faulty note

Faulty hypothetical note to repair (exercise text, not an R-series record):

“R9 proves an attacker stole the desk-a account. The attacker edited every booking. R8 and R11 prove the patron database was exfiltrated.”

This deliberately defective note asserts account theft and an identified actor, all-booking scope, and confirmed data exfiltration without supporting evidence. Rewrite it using only R9, R11, R13 and R8; then list the three claims you discarded. Do not replace the note with an equally unsupported assertion that no transfer occurred.

Objective: repair unsupported attribution and scope. Inputs: R9, R11, R13 and R8. Output: a replacement of at most three sentences with one observed booking fact, one evidence limit and one next authorized check. Then underline or list the three unsupported claims removed from the faulty note. A score of 2 does not equate R8's missing destination detail with proof of no transfer either.

Worked answer: EX025 in the final appendix.

EX026 — Reproducible review

Objective: challenge an overconfident peer conclusion constructively. Inputs: R6, R9, R10 and R12. Output: a four-field review note: claim, directly supported facts, missing discriminator for B19, and authorized way to obtain it. Explain what outcome would change your view. A score of 2 preserves the possibility that training explains B17/B18 while leaving B19 unresolved; it does not assume the staff member is lying.

Worked answer: EX026 in the final appendix.

Worked answers

EX014 — Worked result

Lesson table; scroll horizontally to read all columns →

Answerable question Supplied starting point Authorized next source Result that changes triage
Was the B19 room-change task within the approved training scope effective at 09:03? R9 reports the room change; R10 lists B17/B18 but not B19 Training owner supplies the complete applicable approved plan/version and any B19 addendum through the authorized case route A valid, applicable B19 task approval strengthens the task-scope explanation; a confirmed complete set excluding B19 weakens it. Neither identifies the operator.
What, if anything, is wrong with the selected B19 room and reported time representations? R9 reports a room edit; R11 reports a time concern; R16 requests impact assessment Service owner authorizes a narrow, field-specific comparison; application custodian supplies time-stamped authoritative values and the relevant view against approved targets A supported match, mismatch or missing comparator changes the bounded impact assessment. Further bookings require justified scope; no population count or common cause follows automatically.

The first question tests authorization; the second tests scope. Neither a missing addendum nor an empty narrow query proves the opposite claim without checking source completeness and coverage.

TRI2 evidence layers: In row one, R9 supplies an edit and R10 omits B19 from the supplied plan. A dated, complete, approved task addendum that explicitly names B19 could narrow the task-scope concern; a complete approved list that excludes it would strengthen that concern. Neither identifies the human operator. In row two, R11 is a patron report and R16 asks for impact assessment. A service-owner-authorized comparison of current authoritative values with approved targets could establish a bounded booking mismatch or show that the selected booking is correct. It would not count every affected patron or settle the cause of an audit edit.

EX015 — Worked result

Lesson table; scroll horizontally to read all columns →

Hypothesis Support Contradiction or limit Narrow authorized check Result that weakens it
H1: scheduled training explains the edits R6 records training; R10 names B17/B18 R10 omits B19; R12's recollection is incomplete Training owner supplies the complete approved task set and addenda applicable to the B19 operation A confirmed complete applicable set excludes B19, weakening training as an explanation of all recorded edits
H2: B19 account use fell outside the approved task set R9 records B19 changed under desk-a; R10's supplied plan omits B19 Plan completeness and applicable approval are unconfirmed; R13 does not identify a human Training owner verifies whether the exact B19 task was approved at the relevant time; request session detail separately only for a defined correlation question A verified applicable task approval explicitly covering the B19 room change weakens the outside-plan proposition, without proving who acted or whether the resulting state was correct
H3: the reported time symptom reflects a display or synchronization discrepancy R11 reports that B19's time looked wrong No time-field comparison is supplied; R9 records a room change, not a demonstrated time-display fault Service owner authorizes a time-aligned comparison of the relevant view, stored time and applicable approved time target Contemporaneous view and stored time agreeing with each other but differing from the target weakens a display-only account of that observed discrepancy; an earlier transient fault remains unresolved

The hypotheses need not be mutually exclusive. A good answer identifies what each check can and cannot settle.

EX016 — Worked result

“Decision owner: incident lead. Checkpoint: 09:20 UTC, R1–R16 supplied. Potential harm: R9's B19 edit and R11's patron report could affect room use; no owner-approved state comparison or affected-person total is supplied. Confidence: the synthetic records are available, B19 task authorization remains contested, and neither human operator nor malicious cause is established. Time sensitivity: request timely custodian clarification of R15's 24-hour source policy and unknown deletion anchor; review possible containment separately if continuing harm is verified, because R14's quiet summary has incomplete coverage. Priority: prompt, bounded review, not a NIST-defined category. Next request: seek authorized P-A source/retention clarification and a service-owner impact check. Review: 09:35 UTC or sooner on a verified unexplained new edit or substantiated source-loss concern.” The reasons and trigger matter more than an arbitrary severity number; a single label must not replace the stated evidence limits.

Further reasoning: At 09:20, R9 supports investigating an unexplained B19 edit and R11 supports investigating a reported patron effect. Neither supplies a total impact count. R15 introduces an independent reason for prompt action: detailed booking audit material may be lost under a 24-hour deletion policy. Because the anchor and actual source availability are unknown, the priority note should request an immediate custodian check without publishing an exact expiry. The leading unauthorized-use explanation remains a hypothesis. "Prompt, bounded review" is defensible; a verified fresh unexplained edit or owner-confirmed broader booking damage would justify reassessment sooner than 09:35. A later complete approved task addendum could weaken the authorization concern for B19, but would not remove the need to assess record preservation.

IM2-linked reasoning: R9 reports a B19 edit and R11 a patron concern, while R15 makes source availability time-sensitive without establishing the deletion anchor. There is no authorized target/current-state comparison or complete patron population in R1–R16. Preserving the bounded source can be urgent even when confidence in account compromise and quantified service impact is low. If a later owner-approved state check finds a mismatch, reassess potential harm; if a validated approved change explains B19, weaken the unauthorized-use hypothesis. Neither conditional outcome has occurred in Round B.

EX017 — Worked result

R13 claim/limit: the available summary reports one session in its time range, but contains no device or location field. Missing detail cannot identify a device or person; it is not a finding that the session came from the expected staff device. R14 without T1: the summary says no further edits after R9 within its queried range, but the exact range and filters are absent from R14 itself. R14 with exercise-only T1: the illustrative query concerns desk-a booking edits in application-audit at 09:00 ≤ event < 09:12 UTC; it does not cover later times, another account, ingestion delay or a different source. A proposed next check asks the authorized application custodian for a new bounded query beginning at 09:12 and ending at a documented, sufficiently ingested cutoff, with source, account filters and completeness recorded. Even a zero-row result would be limited to that new scope.

Further reasoning: Canonical R14 states only that its query showed no further edits after R9 within the queried range; its exact start, end, filters, run time and ingestion coverage are not supplied. Optional T1, when explicitly invoked, introduces application-audit, account desk-a and 09:00 ≤ event < 09:12 UTC. It does not cover 09:12 itself, later events, another account or delayed ingestion. To investigate later activity, request a newly bounded query with a documented source, filter, interval, run time, collection status and ingestion-health check. R13's one-session summary lacks device and location details. It does not show that the session was on an expected device, and it cannot establish human attribution. A strong answer preserves both the narrower negative-query result and its untested assumptions.

EX018 — Worked result

Lesson table; scroll horizontally to read all columns →

P-A request element Specific source, object and proposed event interval Policy-anchor / oldest-row question Authority, custodian, storage and state
1. Detailed booking audit relevant to R9 Proposed booking-audit source for B17–B19; requested 08:30–09:20 UTC event-time range, subject to custodian confirmation. R15 gives a 24-hour duration but no verified start anchor or exact expiry. Which relevant row is oldest and still retrievable? Incident lead to approve; booking/application custodian to confirm producer and availability. Restricted collector and copy location remain planned. Proposed, not created.
2. Application-audit summary behind R14 Separately named application-audit source; proposed 08:30–09:20 UTC selection for custodian review. Query filters, producer and index coverage are not supplied. Does its own retention rule differ from R15? What interval and ingestion state did the original query actually cover? Incident lead to approve only minimum necessary selection; application/logging custodian to specify producer, collector and restricted storage. No re-run or export shown. Proposed.
3. P-A authority and copy-control decision Keep both source namespaces, object IDs and selected intervals distinct. Writing P-A does not create a collection or hold. Confirm each source's policy anchor, earliest relevant row, potential loss path and any separate hold capability and effect check. Incident lead owns approval under the fictional rules; name executing custodian and collector only after permission. Document intended restricted location, access list, receipts and review trigger. Proposed, not approved, collected or verified.

A prompt, bounded preservation proposal may be justified before the cause of B19's edit is known because a relevant source might become unavailable, but R15 alone does not prove when deletion occurs, that a hold exists or that a collection has taken place.

Lesson table; scroll horizontally to read all columns →

Comparison at 09:20 Decision value and loss path Owner, state and limit
P-A booking and application audits Could test the B19 edit and R14's quiet-query coverage, if the sources and selection are distinguished. R15 reports a 24-hour deletion rule, but its start point and actual source availability remain unknown. Incident lead seeks approval; application custodian confirms each source, a narrow selection and policy. Proposed, not collected; no current-state or completeness proof.
P-B identity detail Could test whether a session key connects records, if both sources share its meaning. R13 omits device/location and no identity-source retention period is supplied. Incident lead asks identity administrator about availability and minimal fields in parallel. Proposed, not collected; no human attribution.

The comparison does not mean that only P-A matters or that urgency authorizes a bulk identity export. A valid answer may choose a different order if it names a documented loss condition and approval path; it must leave unsupported source coverage and actor identity unresolved.

Further reasoning: A complete P-A request asks which producer emitted the R9 booking audit and which produced R14's application-audit summary before proposing that their rows be joined. It names B17–B19, a bounded event range, minimum necessary fields and the application custodian. The custodian must establish what starts R15's 24-hour clock and whether the oldest relevant detailed row is still accessible. The incident lead owns approval, and the retained object would need its own source, method, access and collection receipt. At the supplied 09:20 state, P-A and P-B remain proposed; P-B can investigate identity-detail availability and its unknown retention in parallel. Neither request is an approved export, an effective hold or a verified copy. The inability to compute a deadline is an explicit result, not a reason to defer contacting the custodian.

MIN2 worked extension:

At 09:20, P-A can be proposed to the application custodian for a bounded booking-audit selection relevant to B17–B19 and R14's source-coverage question, while P-B can ask the identity custodian about available session detail under separate approval. A technical review may need source event ID, booking ID, event time, operation type and an appropriate account/session pointer. It does not automatically need a patron contact address, newsletter membership or unrelated booking records. R15 reports a 24-hour deletion policy but not its anchor or exact deadline. Source preservation, requested field selection, reviewer access and actual collection require distinct statuses and authority. No completed export or active retention hold is supplied.

HOLD2 worked extension:

At 09:20, the P-A card can request an authorized, bounded check of relevant booking-audit and application-audit sources, explicitly distinguishing their producers and retention semantics. The three-row table asks (1) which source and interval are involved; (2) what starts R15's 24-hour policy clock and whether the oldest needed row is still retrievable; and (3) who approves, applies, verifies and stores any authorized copy or source control. An effective source-hold receipt, if the custodian later supplies one, would identify its source, object set, interval, authority, custodian, reported start, term/review, effect observation and limit. The P-A/P-B comparison retains separate custodians and unknown P-B retention. Both requests remain proposed in R1–R16. The isolated HOLD2 result does not prove Harbor Library preserved any data, and a missing oldest row would require source-coverage investigation, not a fabricated deletion time.

EX019 — Worked result

Lesson table; scroll horizontally to read all columns →

Collection-note field Model entry before a learner runs the optional fixture
Case/item and state HL-IR-001/E1; planned classroom transcription, not a completed export
Source and event range Synthetic book records R9 at 09:03 UTC and R14 at 09:12 UTC; R14's underlying query interval is unspecified unless the separate T1 card is explicitly used
Collector and authority Learner analyst, within the fictional incident lead's classroom scope for supplied records only
Collection time and location Not applicable until a file is actually created; then record the learner's real local time, file path and access restriction
Transformation and original Fixture transcribes two rows as UTF-8 text; the book table is the retained source of the classroom transcription
Limitation No production log was acquired; a saved file cannot prove the source system's completeness, clock or identity attribution

State criterion within the existing 0–2 rubric: A completed local E1 file, a second-file failure and a planned collection are different states. Record only the file, time, integrity measurement, transfer or disposal actually evidenced; mark unrecorded or unperformed fields explicitly. Do not claim that failure to create the second file invalidated or erased an already created first file. A retained copy is not an applied source hold. This criterion adds no row-count requirement, exercise ID or production permission.

If the learner ran fixture.py, replace the planned state with completed local fixture, record the actual creation time and scratch path, and attach the measured 235-byte SHA-256 value from the lab. Never invent a 09:20 collection time simply because the scenario's discovery checkpoint was 09:20.

MIN2 worked extension:

The original E1 collection note describes a synthetic transcription and distinguishes a planned local exercise from a file actually created by the learner. A separate review-view manifest would record the original E1/book source pointer, bounded permitted rows, retained fields, excluded fields, intended analyst role, transformation method and its version. If no derived file was made, the creation time, location and digest remain not applicable. If a local file was produced, record its actual creation time and compute its own measured digest if required; the original E1 digest does not attest changed bytes. The derivative remains a working selection, not an authenticated production export or evidence of source completeness.

HOLD2 worked extension:

E1 is a synthetic transcription whose actual local creation time and measured digest belong in the collection note only if the learner actually generated the file. A separate source-hold ledger reference could identify a planned control for a named audit producer and record status as not supplied because no actual source hold is in the canonical Round B evidence. If a later fictional custodian response were provided, it would require its own control identifier, applicable authority, scope, reported activation, non-destructive check and release/review fields. The E1 digest supports limited consistency of E1's saved bytes; it cannot establish that the underlying source stopped deleting records. A source-hold release entry and a collected-copy retention decision remain different records. The original E1 table format and its source/copy distinctions must be preserved.

EX020 — Worked result

Lesson table; scroll horizontally to read all columns →

Claim Supported? Reason
The named E1.txt produced equal SHA-256 values at the two recorded checks Yes, if the learner actually measured and retained both values This is the direct comparison of the checked bytes under the stated method.
No byte change was detected in that named copy between the checks Yes, within the hash method and trustworthy-check assumptions Equality supports a limited consistency finding, not a broader provenance claim.
The original application log was complete and true No E1 is a transcription; the digest cannot reveal omitted or false source rows.
The source clock and actor identity were correct No A digest has no independent clock or identity context.
The export or collection had organizational authority No Permission is a decision record, not a property of the digest.

Specify the file, method and check times. Two matching values copied from the same mistaken note would not become proof of authenticity.

Further reasoning: If the learner actually created E1 and repeated the stated SHA-256 checks, the equality supports consistency of the named copy bytes at those checks, subject to method and record trust. The modified E1 variant with a different digest establishes only that the modified teaching bytes differ from those of E1. The source layer is not authenticated by the digest: E1 was transcribed from the synthetic R9/R14 table, and the underlying canonical R14 query manifest is incomplete. The interpretation layer also remains uncertain: none of the copied bytes proves who controlled desk-a or whether B19 was authorized. A 2-point answer must name the file and distinguish copy consistency from provenance, complete coverage and permission; generic claims that the evidence is "verified authentic" fail that boundary.

MIN2 worked extension:

Matching E1 SHA-256 values at two actual checks support only a bounded consistency claim about the specified copied bytes and method. They do not show that the source was complete, correctly timed, lawfully collected or authorized for broader distribution. A technical reviewer may need a derived view with unnecessary contact fields excluded. The view's field policy, row scope and transformation receipt must be recorded separately from the raw copy's integrity comparison. Neither redaction nor a digest establishes anonymization or human attribution; those require their own substantiation.

EX021 — Worked result

Lesson table; scroll horizontally to read all columns →

Order Record and supplied UTC time Source and time kind Specific uncertainty or unsupported inference
1 R9, 09:03 Booking audit; reported B19 edit time The account label does not identify the human or prove the edit was authorized.
2 R10, 09:04 Training plan; supplied plan-record time This is not an effective approval time or proof that the plan was first written after R9. Ask the training owner for the applicable version and approval evidence.
3 R12, 09:08 Staff interview note; supplied note time The recollection does not establish the time of every recalled action, the completeness of the task list or who performed the B19 edit.
4 R15, 09:14 Retention notice; supplied notice time The source's retention-start anchor and actual deletion deadline remain unknown; this notice does not start a new 24-hour clock.
5 R16, 09:16 Service-owner note; supplied note time Requesting an impact check and describing pause authority do not establish that the check or pause was approved and executed.

Separate availability note: All five cards first become available to the learner with R9–R16 at the simulated 09:20 review. None may justify the earlier 09:05 R1–R8 decision. These are differently typed source-card times, not five instances of a single operation clock. No local copy-creation time, source-ingestion time or completed collection follows from this table.

The supplied order is R9 → R10 → R12 → R15 → R16. It cannot establish who made the B19 edit, whether it was authorized or whether it caused the patron's report. R10's record time does not date the plan's first authorship or effective approval; R15's notice time does not anchor deletion. The training owner and source custodian would need to supply those distinct missing records through the authorized route. Those requests remain proposed.

If optional T1 is used, its event-time interval is 09:00 ≤ event < 09:12 UTC. It excludes an event exactly at 09:12 as well as later events. Its specified window and account filter do not establish ingestion completeness. Without T1, canonical R14's exact window remains unspecified. A full-credit answer gives a limit for each of the five rows, not merely a correct sort.

EX022 — Worked result

“At 09:20 UTC, HL-IR-001 concerns the room-booking service: R9 records B19 edited and R11 records a second patron report. H1 can explain the earlier training edits, while H2 remains possible for B19 because R10 omits it and R13 gives no device detail. The fictional incident lead authorizes classroom preservation of supplied records and asks the service owner to approve a proposed, narrow real-world impact check; no production account or data change has occurred. The analyst will ask the training owner about any approved B19 addendum and record source and query limits. Review with the incident lead at 09:35 UTC or sooner if another verified unexplained edit appears.” A handoff must tell the receiver what was approved, what remains only a request and what would change the next decision.

Further reasoning: At 09:20 UTC, case HL-IR-001 concerns Harbor Library room bookings: R9 reports a B19 edit, while R11 records a second patron complaint. The supplied training plan in R10 names B17/B18 but not B19, so authorized training and use outside the plan remain competing explanations; the operator is not identified by R13. R14's quiet query does not establish complete subsequent activity, and R15 reports a 24-hour detailed-audit deletion policy without a verified anchor or deadline. The incident lead proposes a bounded P-A availability/preservation request through the application custodian and a service-owner impact check; no production copy, hold or account restriction is claimed completed. Review at 09:35 UTC or earlier on a verified unexplained booking change.

The five sentences label the evidence, assessment, unanswered questions, proposed action and review trigger. A classroom preservation authorization, if separately recorded, must not be misreported as a production export.

HOLD2 worked extension:

At 09:20 UTC, HL-IR-001 concerns Harbor Library room bookings: R9 reports a B19 edit, R10's supplied training plan omits B19, and R11 records a patron concern. Authorized training outside the listed tasks and use of the account beyond approved scope remain competing explanations; R13 lacks details needed for human attribution. R14 provides only a bounded quiet-query summary, while R15 states a 24-hour deletion policy for detailed booking audit whose starting anchor and oldest-row availability remain unknown. The incident lead proposes that the application custodian confirm P-A's source, hold or copy feasibility and retention semantics, with P-B identity-detail availability separately proposed; no production copy, source hold or account restriction is established. Review with the incident lead at 09:35 UTC or earlier if a verified new edit or source-loss warning requires a fresh decision.

The five sentences remain an internal handoff, not a claim of completed collection. The proposal does not assume a hold mechanism exists or borrow HOLD2's fictional positive status.

EX023 — Worked result

Proposed request: ask the service owner to approve a named analyst to compare booking-edit audit rows for the short 08:30–09:20 UTC window with the authoritative booking state and relevant help-desk ticket identifiers, using only booking ID, changed field, old/new value, event UTC time and approved target where available. If approved, the application custodian would supply a restricted copy and record the query filter, source and collection time. The expected result schema is booking ID | audit row | current value | owner-approved value or pending | confirmed mismatch / unresolved. The learner can already point to B19's R9 edit but cannot supply a final count or approved target from R1–R16. Keep patron names and unrelated bookings out of the classroom output and limit the copy to the approved reviewers.

Further reasoning: A well-scoped request proposes comparing the B19 audit row, any approved relevant booking targets, the current authoritative state and the patron-facing information needed to evaluate R11. The service owner would approve the business comparison; the application custodian would then confirm the exact source and selection.

Hypothetical outcome A — verified match within the selected frame: The owner-approved target and the current authoritative B19 value are present with their applicable versions, time and selection receipts; the values agree under the approved comparison rule. This supports a match for that selected state, not proof that every patron-facing consequence was correct or that the booking edit was authorized.

Hypothetical outcome B — verified mismatch within the selected frame: Both trusted comparison values and their relevant versions are available, but the current authoritative B19 value differs from the approved target. This supports a specific state discrepancy for B19, pending owner review of its consequence; it does not identify an attacker or estimate all affected patrons.

Hypothetical outcome C — unverified comparison: The approved target, current value, provenance, applicable time or required selection evidence is missing or not trustworthy. Do not label this a clean match or a confirmed mismatch. Request the missing bounded information or keep the result unverified.

In all three branches, the unit is the selected booking ID, not the help-desk ticket or an affected person. The requested view excludes unrelated patron names, newsletter fields and unapproved bookings. None of these three hypothetical outcomes is observed in R1–R16.

IM2-linked measurement contract: Count distinct selected booking IDs only after declaring the owner-approved target, source version, current authoritative state and comparison time. Keep the reason each row was selected. B17–B19 may form a bounded inquiry frame, but R1–R16 have no completed authoritative comparison. Proposed result columns are booking ID | audit pointer | report pointer | approved target/version | current value/time | match/mismatch/unverified | limit. A mismatch for one valid tested row is not an estimate of affected patrons. In the separate invented IM2 exercise, six encountered IDs yield two matches, two mismatches and two unverified classifications; four reports reference three distinct booking IDs. X04 has no report but is a mismatch under the fictional comparison; X05 has a report but lacks a target. Removing X04’s target changes its label to unverified without repairing any state. These are classroom outcomes only.

EX024 — Worked result

Lesson table; scroll horizontally to read all columns →

Hypothetical new observation Who verifies it and why it changes triage Decision response
A future source-verified unexplained booking edit after 09:20, or an owner-confirmed material booking mismatch The application custodian checks source, selection and clock; the service owner checks the business target and affected scope. The signal could raise continuing-harm or verified-service-impact concern without naming an attacker. Incident lead and relevant service/identity owners reconvene promptly to compare proportionate, separately authorized containment options. No control executes automatically.
A future custodian-confirmed inability to protect or retrieve a necessary selected source item, or a substantiated warning that such an item is about to be lost The application custodian identifies source, event interval, oldest expected row, selection and check time; the team distinguishes actual loss from wrong source or incomplete retrieval. This changes preservation urgency or uncertainty, not cause attribution. Incident lead seeks an authorized alternate preservation route and escalates through the approved channel, with a near-term review. The exact R15 deletion deadline remains unknown.

Decision-time acceptance: For either hypothetical branch, the incident lead records the verifier, time of the qualifying observation, owner contacted, decision proposed, separate approval status and next review. At the supplied 09:20 checkpoint, 09:35 UTC remains the planned review; if either branch becomes source-verified earlier, reconvene promptly at the time of that verified trigger rather than waiting until 09:35. A confirmed source-preservation difficulty calls for a custodian/owner escalation and a separately authorized alternative, not automatic containment. An unexplained edit or owner-confirmed mismatch calls for a separate, proportionate containment assessment, not an assertion that a control was applied. Neither trigger, decision nor operational effect is already observed in R1–R16.

Neither observation is part of R1–R16. A confirmed sensitive-data disclosure would also raise harm, but no supplied record establishes one. The triggers are evidence requirements, not predictions.

HOLD2/TRI2 interpretation: The first pair prompts a human containment review; the second prompts a distinct preservation decision. A missing item or unavailable custodian does not itself prove malicious deletion, and neither future observation belongs to R1–R16. Preserve both pairs as hypothetical, with the verifier and next review explicit.

EX025 — Worked result

“R9 records a B19 booking edit under desk-a, and R11 records a patron's report. R13 omits device and location detail, while R8 lacks destination detail; neither establishes who controlled the session or whether information was transferred. The incident lead will seek a service-owner impact comparison and authorized identity details before revising scope or attribution.” Removed unsupported claims: a named attacker stole the account; every booking was edited; and the patron database was exfiltrated. The correction does not replace those false positives with an equally unsupported claim that no transfer occurred.

EX026 — Worked result

Lesson table; scroll horizontally to read all columns →

Review field Model note
Peer claim “R6 and R10 confirm H1 for all edits.”
Directly supported R6 says training was scheduled; R10 lists B17 and B18 in the plan. R9 separately records a B19 edit; R12 reports that the staff member does not recall it.
Missing discriminator A complete approved task list or dated addendum naming B19, plus a narrow authorized check of the account and application edit context.
Possible update A valid pre-existing B19 addendum would strengthen H1 for that edit, though identity still needs checking; a confirmed complete plan excluding B19 would weaken H1 for B19.

Treat the conclusion as unresolved until the owner and record checks are assessed. A constructive review explains how to change the conclusion; it does not accuse the staff member because recollection and audit attribution can both have limits.