applied · Level 3

AI prospect research without invented intelligence

A practical account-evidence workbook: entity, origin, time, unknowns and the outreach boundary.

By Mickarle Sean Junior Wagstaff-Irons - Micky Irons

  • Level 3Building
  • 720 min
  • 8 chapters
  • Online course and PDF workbook
original-sector-illustrationapplied / 03

Read, practise and review

Your next step

Start this course online

Register your start, then work through the lessons and exercises at your own pace. You can download the PDF separately. Where a scored exam is available, you will find it after the lessons.

Register or resume your course

Mickai privately records this registration and sends its team a course-start notification. It records your intention to start, not proof of attendance or completion. Our course-start privacy notice explains the details and the 30-day resume cookie.

Follow the Mickai company page on LinkedIn

You can start learning without subscribing or following. Newsletter email confirmation and provider enrolment are recorded separately.

Start with the essentials

The short answer

Start with a specific account question and permitted sources. Separate company identity, evidence origin and recorded time before writing narrow facts, labelled hypotheses and explicit unknowns. Remove unnecessary personal detail, preserve objections and obtain accountable review for the intended use. Pipit is fictional: a complete internal evidence packet establishes neither real account truth nor permission to contact anyone.

What you will learn

  • Define an account question and necessary fields without broad personal profiling or assumed source permissions.
  • Distinguish the target company, a similar-name entity and unsupported operating-site inference.
  • Trace source dependence and separate event, publication, retrieval and as-of dates.
  • Reject invented budget, buyer quotation and purchase intent while retaining useful hypotheses and explicit unknowns.
  • Prepare a minimum source-linked brief and separate actual privacy, source-use, supplier and outreach decisions.
  • Rehearse a controlled withdrawal, stop, current-revision recovery and fresh internal review without restoring stale authority.

Who it is for

Adult sales leaders, account managers, revenue operations and bid teams. No real companies, buyers, contacts or private account records are used.

Before you start

  • Understand basic AI limitations and the difference between evidence and generated wording.
  • Understand privacy and data minimisation before considering any actual personal-data use.
  • The optional offline lab uses Python3.10+ standard library. Printed source cards and worksheets provide a non-AI route.

Jurisdiction and study scope

These are selected reference pathways. Choose the relevant pathway before applying the exercises. This does not determine applicable law or provide worldwide coverage.

All pathway limits
  • Actual applicability unresolved: Identify the controller, proposed company/personal fields, source rights, provider, recipient, subscriber, channel and territory. The fictional lab selects no actual legal basis or permission.
  • Scoped UK account-research reference: Selected Companies House, ICO and IPO guidance only. Current ICO direct-marketing updates and March2026 basis terminology are distinguished from older DUAA-review pages. Reasonable-period and earlier notice-event limits remain. PECR22 and CDPA29A operative pages were not successfully retrieved; qualified review remains open.
  • Other territory or cross-border use: The retrieved FTC guide supplies a selected US federal commercial-email contrast, not state-law, call/text or UK clearance. EU/ePrivacy and other-country applicability are unreviewed; no worldwide conclusion follows.

Owner-commissioned original teaching prepared with AI assistance. Pipit companies, cards, faulty draft, correction and review token are authored fiction. No actual account, buyer, model output, practitioner qualification, source right, lawful basis, identity verification or outreach approval is claimed.

Learn in your browser · Chapter 01 of 08

01

Start with an account question and an access boundary

Define a narrow account decision before admitting sources or proposed uses.

An account question should make an internal decision easier without pretending to know a buyer's private situation. Pipit, a fictional seller of reusable transit containers, wants to understand whether an evidenced container-related project merits further account planning. Its target is invented Westhaven Returnable Containers Ltd, teaching identifier WR-C01. That question is narrower than who will buy, how much they can spend or which person can be persuaded. Begin by writing the receiving decision, company subject, relevant period and kind of evidence that could change the decision. A missing answer can prevent a team from building a sales plan on invented intelligence.

A source-admission boundary comes before a summary. The supplied packet has local, originally authored company cards, deliberately faulty draft wording and metadata-only exclusions. It is not a collection from real registers, websites or individuals. P01 describes fictional registry-style status; P03 describes a proposed expansion; P09 describes an unresolved-rights report whose body is absent. Inspect the permitted teaching extracts without inventing the missing report. Source admission asks whether material belongs to the question and whether the proposed action has been reviewed. Neither a plausible title nor a convenient link establishes what an unseen document says. Record the absence rather than fill it.

Separate four actions often collapsed into permission to research. Consulting a source, copying an extract, sharing a quotation and submitting material to an AI provider are different proposed uses. Material may be available for reading while redistribution or supplier processing remains unresolved. The source group ipo-account-source-rights describes limited UK copyright exceptions, including a non-commercial research context for the described text-and-data-mining exception. Commercial account planning cannot simply adopt that context. The March 2026 copyright and AI report is policy material, not enacted permission for a sales workflow. Its presence in a register does not settle licences, database restrictions or access terms.

Build an admission ledger with publisher, source type, entity, publication date, retrieval date, intended use, permitted extract and unresolved rights. Add a specific locator when an extract is available, and distinguish metadata from body text. The ledger makes an unresolved decision inspectable rather than determining its legality. For P09, record that only title and restriction metadata are supplied, the body is absent and no report finding is available. Do not fill an empty extract cell with a model-written approximation. Actual material obtained later would need its own admission decision rather than inheriting a fictional card's teaching status or a convenient internal label.

Question design controls how much information is necessary. A request for every fact about Westhaven invites irrelevant biographies, rumours and guessed pain points. A question about a container-related project needs entity identity, the stated proposal, later updates and time limits. The ICO group ico-lead-generation treats collecting and enriching information for direct-marketing purposes as relevant processing, rather than making privacy a concern only when a message is sent. Calling a file internal does not automatically remove its actual purpose. Here, no personal-data collection occurs and no legal basis is selected. Practise defining the proposed task before any actual collection decision or contact-list creation. For actual personal data, ico-notice-timing distinguishes direct information at collection from indirect information within a reasonable period, no later than one month, subject to applicable earlier first-communication or envisaged-disclosure limits. Exceptions have conditions; website publication alone is not an automatic substitute. The page displays a DUAA review notice. This paper task makes no actual notice or legal-basis decision.

An average worked brief asks a hypothetical assistant to produce an exhaustive buyer profile and estimate budget from the sector. It looks useful because every heading is filled. The advanced brief asks a narrower question, lists P01 and P03 as supplied synthetic material, identifies the later-update check and leaves P09 unread. An empty budget field is an honest gap rather than a drafting defect. Both versions can be fluent; only the second makes its evidence boundary visible. The comparison concerns reasoning quality rather than a particular model. No assistant is run here, and a deliberately faulty draft is not an observed model failure.

Use a six-part paper prompt to make responsibility explicit. The role is internal account-evidence editor. Constraints name the fictional target, local extracts, excluded details and absence of outreach authority. The process separates propositions, entity matches, origins and dates before drafting. Verification requires a passage for every material company statement and a record of unresolved conflicts. The schema contains dated statements, sources, historical context, hypotheses, unknowns and next checks. Failure handling stops unsupported assertions entering the factual brief. These instructions organise a human paper task; they do not create access rights, authenticate a reviewer or establish a model's reliability with real accounts.

Technical capability is a poor substitute for admission. A browser can show a search result while a report body remains unavailable. A parser can copy text without deciding whether that copying is appropriate. An AI service can accept an upload without establishing that the team should send it. Name the proposed recipient, purpose and necessary material before actual supplier review. Do not assume that a consumer interface and an approved organisational service have identical retention or training arrangements. This chapter recommends no provider. Its output is a question for the person responsible for the intended use, alongside narrower work that can continue with synthetic material.

The group nist-genai-evidence offers a voluntary US technical reference for risks including confidently false content and source verification. Use it to motivate passage checking and narrowly specified tests, not to announce UK legal compliance. A plausible citation may point to an irrelevant passage, a copied announcement or material never read. A confident tone does not resolve those problems. This workshop examines supplied statements against a finite packet rather than benchmarking a model. Keep the distinction between an authored challenge and observed output whenever explaining what was detected. A well-written paper answer is evidence of this answer, not a general reliability score.

A useful stop is specific enough to support recovery. Instead of research unavailable, say that P09 has no permitted body extract and cannot support a report finding. Identify which missing fact could change the decision and what admitted evidence would answer it, rather than demanding broad access to everything. A next paper action might compare P03 with P05 or draft a rights question for the account owner. Do not convert planning into permission to retrieve a restricted report. Preserve what was attempted, why it was unsupported and which narrower work remains possible. This makes the gap actionable without pretending it has been resolved.

Sharing changes how an evidence record can be misunderstood. Place synthetic origin and internal-purpose labels beside the brief, not only in an introduction that may be lost. Retain an excluded-material entry without reproducing excluded content. Give the colleague enough information to distinguish an unread source from a source read but not supporting the assertion. This prevents two opposite mistakes: presenting absence as proof that nothing exists, and presenting a report title as proof of its supposed contents. A colleague can ask for a different source or a narrower claim when the boundary is visible, instead of unwittingly repeating an invented finding.

Finish with a small deliverable: a question-led admission record for WR-C01. Identify which supplied cards answer parts of the question, what each supports, which proposed uses are unresolved and which gaps remain. It is not a buyer dossier, CRM update, contact list or outreach licence. The frozen Pipit contract describes finite wording and assumed internal review, not real rights or professional approval. This chapter's exercise remains on paper and claims no executed lab result. Before actual use, source rights, personal-data purpose, supplier handling and organisational responsibilities remain separate decisions. Keeping those limits is part of making an account brief another person can challenge.

  1. State the internal question and receiving decision.
  2. Choose necessary company fields.
  3. Inventory available extracts and absent bodies.
  4. Separate reading, copying, quotation and provider use.
  5. Allocate passage checks and unresolved decisions.
  6. Deliver a dated packet with unknowns.

Scroll sideways to see every column.

Start with an account question and an access boundary · Table 1
Average draftAdvanced repair
Exhaustive profile and invented report findingUse the narrow WR-C01 project question. P01 supports dated synthetic status, not an operating site. P03 supplies a qualified proposal needing later-update checks. P09 is metadata only: body absent, findings unavailable, rights unresolved. Separate source reading, copying, quotation and provider processing. The prompt includes role, constraints, process, verification, schema and failure handling. No buyer profile, budget or outreach permission is created.
text · 6 lines
ROLE: Adult internal account-evidence editor.
CONSTRAINTS: Synthetic Pipit packet only; no actual person, company, collection, upload, model or outreach.
PROCESS: State the internal question and receiving decision. Choose necessary company fields. Inventory available extracts and absent bodies. Separate reading, copying, quotation and provider use. Allocate passage checks and unresolved decisions. Deliver a dated packet with unknowns.
VERIFICATION: Compare exact admitted passage, entity, time, qualifier and origin; preserve absent material and rejected wording.
OUTPUT SCHEMA: assertion; entity; card; origin; publication; retrieval; asOf; supportedScope; unknown; omission; nextCheck; actualUse=NOT_ASSESSED.
FAILURE MODES: Hold unread bodies, wrong subjects, unsupported budgets/quotes and external action. Paper prompt only; not executed.

Start with an account question and an access boundary

Supported work continues with the gap retained separately.

Account questionNecessary fieldsSource admissionPermitted extractEvidence packetUnresolved rights
Steps
  • Account question: Internal paper question
  • Necessary fields: Scoped check
  • Source admission: Supplied synthetic record
  • Permitted extract: Explain the decision
  • Evidence packet: No actual approval
  • Unresolved rights: Retain the unresolved branch
Relationships and conditions
  1. Account question to Necessary fields: check then continue
  2. Necessary fields to Source admission: check then continue
  3. Source admission to Permitted extract: check then continue
  4. Permitted extract to Evidence packet: check then continue
  5. Source admission to Unresolved rights: unresolved: retain gap

Diagram text equivalent: Account question then Necessary fields then Source admission then Permitted extract then Evidence packet. From Source admission, retain the separate Unresolved rights branch. It is not a supported fact or an actual-use approval.

Try it yourself · Activity 01

25-35 minutes, unpiloted estimate

Prepare an admission ledger without an unread report

Prepare a ledger for P01, P03 and P09 answering whether project evidence merits internal planning. Add a six-component paper prompt. No P09 body is supplied.

  1. State the internal question and receiving decision.
  2. Choose necessary company fields.
  3. Inventory available extracts and absent bodies.
  4. Separate reading, copying, quotation and provider use.
  5. Allocate passage checks and unresolved decisions.
  6. Deliver a dated packet with unknowns.

Identify the assumption that changed your conclusion and one unresolved check. How would you keep that gap visible when sharing the brief?

Worked answer

Filled paper admission ledger, using only original fixture metadata. The decision is whether supplied WR-C01 container-project evidence merits further internal planning as of 1 October 2026; not a buyer profile or permission to contact. P01 | publisher/source: course-authored fictional registry-style card, kind company | entity WR-C01 | origin register-card | revision 1 | event 1 September 2026 | publication 1 September 2026 | retrieved 1 October 2026 | body supplied: dated active status and registered Office Token RO-1; operating site not identified | intended use: narrow identity/status context | extract: retain that scoped statement and its source/date; no operating-site inference | rights: supplied local synthetic teaching extract; no actual register licence or real source-rights verification established. P03 | publisher/source: course-authored fictional company-announcement card, kind company | entity WR-C01 | origin expansion-announcement | revision 1 | event 3 May 2026 | publication 3 May 2026 | retrieved 1 October 2026 | body supplied: proposed second site, subject to review, with no opened site, approved purchase or budget stated | intended use: historical project question checked against later P05 | extract: preserve proposal/qualification and cancellation relationship | rights: supplied local synthetic teaching extract; actual commercial source-use decisions remain unverified. P09 | publisher/source: course-authored restricted-metadata card titled Fictional account market report | entity UNKNOWN | origin absent | revision 1 | event and publication absent | retrieved 1 October 2026 | body absent | intended use: record the source-use gap only | extract: restriction metadata, not a report finding | rights/access unresolved; HOLD any supposed report summary, quotation or provider upload. The title does not establish contents. Case-specific six-component paper prompt: ROLE: Adult Pipit internal account-evidence editor preparing a WR-C01 planning packet. CONSTRAINTS: Original synthetic cards only. WR-C01 and WE-C02 are invented IDs. Admit the supplied P01/P03 extracts; keep P09 body absent. No actual account research, personal dossier, source-rights clearance, model, upload, CRM or outreach. PROCESS: State the project question; fill the admission ledger; distinguish reading, copying, quotation and provider processing; split statements; check entity, origin and time; compare P03 with P05 before drafting present claims. VERIFICATION: Require a permitted passage for every material fact. P01 does not prove an operating site; P03 alone does not prove current opening, budget or purchase; P09 supplies no body finding. Retain rejected assertions and unresolved decisions. OUTPUT SCHEMA: accountQuestion; entity; asOf; sourceCard; sourceType; origin; revision; eventDate; publicationDate; retrievedAt; intendedUse; admittedExtract; rightsUnknown; supportedStatement; unknown; nextCheck; outreach=NOT_ASSESSED. FAILURE MODES: Stop unread-report findings, unsupported budget/quotation, wrong-entity joins and actual-use requests. Name missing evidence or rights decisions and continue only narrower synthetic work. This is supplied paper wording, not an executed prompt or observed model result.

Try it yourself · Activity 02

15-20 minutes, unpiloted estimate

Six answered chapter review questions

Attempt before reading answers. 1. Why reject an exhaustive buyer profile as the first task? 2. What can P09 establish? 3. Does a locator establish rights? 4. Why separate upload from reading? 5. What follows a source hold? 6. What does NIST establish here?

  1. Answer each question with a reason.
  2. Compare your reason with the supplied record and chapter method.

Which answer did your initial wording widen beyond the evidence?

Worked answer

1. It expands scope into unnecessary and speculative personal fields before identifying the company evidence needed for the decision. 2. Only supplied restriction metadata and body absence, not any unread report finding. 3. No. Location, supported content and permission for the intended action are separate checks. 4. Supplier processing changes the recipient and action, needing actual purpose, handling and relevant rights review. 5. Name the missing evidence and unresolved use, then continue narrower synthetic work rather than inventing a body. 6. A voluntary technical rationale for source checking, not UK legal clearance or measured model reliability.

Apply what you learned · Practice

Which first task respects the boundary?

Explanation and answer

Prepare an internal WR-C01 project-evidence packet. The question and admitted synthetic extracts define a bounded internal task.

Practice only. This does not count towards your exam or certificate.

Next chapter

Learn in your browser · Chapter 02 of 08

02

Resolve the company without building a personal dossier

Bind statements to the right company and retain only task-relevant information.

Identity is a relationship between a statement and its subject, not a spelling match. In Pipit's packet, WR-C01 identifies fictional Westhaven Returnable Containers Ltd and WE-C02 identifies fictional Westhaven Events Ltd. Both names contain Westhaven, but the entities are distinct. These are invented teaching identifiers, not Companies House numbers or verified registrations. A statement about an event at WE-C02 cannot become a WR-C01 statement because a search result contains a familiar word. Write the subject identifier beside each proposed assertion before considering whether it is commercially interesting. Correctly copied wording attached to the wrong company still misleads the receiving account manager.

Separate legal entity, trading name, group, site, role and individual. A trading name needs evidence of its relationship to a legal entity. A group announcement may concern one subsidiary rather than every group company. A site description may name a location without establishing which entity operates it. A role label may still refer to an identifiable individual. Expose the joins that a fluent paragraph hides by giving subject, relationship and relationship evidence separate worksheet fields. Leave a join unresolved when supplied material does not establish it. Do not choose the most commercially convenient interpretation just because it makes a cleaner account narrative.

P01 illustrates office versus operation. Its registry-style card records active status and registered Office Token RO-1 on 1 September 2026. That does not establish a working warehouse, a container-handling site or a place a salesperson should visit. The group companies-house-account-identity describes register searching and June 2026 checks and correction powers. Do not teach that Companies House never checks material. Equally, filing checks do not independently establish every commercial conclusion derived from a filing. Status, filed content and an assertion about operating activity are different propositions. B01 in the frozen contract tests only the unsupported operating-site inference; active status remains contextual evidence.

P02 supplies a separate entity's demonstration schedule. An average draft joins it to WR-C01 and describes a target-company opportunity. The first repair removes the false join rather than lowering a confidence score. The advanced worksheet records WE-C02 as P02's subject and states that the supplied schedule does not establish target attendance. It invents no corporate relationship between the entities. Current target procurement remains unknown on this evidence. This is a positive analytical result because it names exactly why an attractive inference should not enter the brief. It is not a finding that WR-C01 definitely will not attend any event.

Company documents and personal information can coexist. Under ico-personal-data-quality and ico-b2b-boundaries, identifiable business-contact information can be personal data; a business context does not make every detail impersonal. Conversely, not every fact about a limited company is an individual's personal data. Review fields and context rather than assigning one label to the whole document. A role, location and distinctive circumstances may identify someone without their name. Removing the name is therefore not an established anonymity assessment. Retaining a relevant company identifier also does not justify retaining unrelated biography. An actual workflow needs its own applicability and purpose assessment, which the synthetic task does not perform.

P08 is metadata-only and excluded. It indicates an irrelevant personal-interest category without supplying a person's name, hobby or contact information. Do not invent the missing detail to make redaction more vivid. Record that the category does not answer the container-project question and omit it from the shared factual brief. This is minimisation by purpose: ask whether a field is adequate, relevant and necessary for the stated task. A supposed rapport opportunity is not project evidence. A salesperson's curiosity is not itself a necessity argument for a personal dossier. The exercise demonstrates an omission reason without processing any actual person's information.

Minimisation should not destroy evidence needed to inspect a company claim. A document containing both an announcement and an irrelevant biography calls for justified field-level handling, not automatic deletion of everything. Preserve enough publisher, date, subject and locator context to inspect a retained company statement. Explain the category omitted and the task-based reason without copying the excluded detail into the explanation. This avoids retaining a dossier inside a supposedly redacted appendix. In this finite case every supplied item is synthetic. The paper method does not determine a real lawful basis, retention period, anonymisation result or permission to redistribute extracts from an actual document.

Company identity and contact permission solve different problems. Identifying WR-C01 does not identify an appropriate buyer, establish preferences or authorise an approach. ICO B2B guidance distinguishes corporate subscribers from individual subscribers, including relevant sole-trader and partnership contexts. A generic business address does not remove all channel rules, and public availability is not automatic consent. The older B2B page's basis-count wording should not replace the March 2026 legitimate-interests guidance; this course does not teach a memorised basis count. Keep legal-basis, subscriber and channel questions separate. No actual classification or message permission is selected by this chapter's company-fact worksheet. Several older ICO pages display DUAA review notices; retain those currency limits rather than claiming the whole register has one update date.

Use a six-step subject method. Identify the grammatical subject of the assertion. Assign the supplied entity identifier only where the source supports it. Distinguish entity, site, role and person. Test any alias or group relationship against evidence. Assess which fields help the stated account question. Finally retain supported company facts and record unresolved joins or excluded categories. This order matters: stripping a name after an incorrect company join does not repair the commercial statement. Nor does a correct company identifier make unnecessary personal information appropriate for sharing. Each check needs its own explanation, so an adult reviewer can see which problem was actually resolved.

Consider a contaminated table with three rows: WR-C01 is active according to P01; WR-C01 attends WE-C02's demonstration according to P02; WR-C01's buyer is likely interested because of P08. The first row can remain narrowly dated and synthetic. The second belongs to the other-entity record rather than the target. The third has no supplied buyer identity or interest evidence and relies on excluded metadata. A repaired table retains the status statement, a wrong-entity rejection and buyer unknowns. It does not quietly convert rejected material into a softer account narrative about apparent opportunity. Unknown is a useful result when the evidence cannot support the desired join.

The verification schema should expose assertion, source card, source subject, target subject, proposed relationship, relationship evidence, retained fields, omitted categories and unresolved questions. A numerical name-match percentage cannot replace relationship evidence. It may describe a string comparison but does not establish an entity relationship. A future automated matcher would need evidence and review appropriate to its actual task; none is run here. The frozen lab's bind_entity operation uses supplied identifiers and reports a synthetic fixture binding, not real-company verification. This chapter's paper exercise asks for reasons about a particular mismatch rather than a claim to resolve every ambiguous company in an actual register.

The resulting brief is useful because it is precise about the company and modest about individuals. A colleague can see what P01 supports, why P02 belongs elsewhere and why P08 is omitted. They can also see that operating site, buyer identity, current project and outreach status need their own evidence or review. The advanced result is the smallest traceable company record that answers the defined question without hiding gaps. Real register verification, source-use rights, privacy decisions and contact review remain open. Completing a synthetic worksheet, receiving an assumed lab review or correctly matching an invented identifier settles none of those actual responsibilities.

  1. Name each assertion subject.
  2. Bind supplied IDs without guessing aliases.
  3. Separate entity, site, role and person.
  4. Test the proposed relationship.
  5. Justify retained fields and omit unnecessary categories.
  6. Record unresolved joins and buyer unknowns.

Scroll sideways to see every column.

Resolve the company without building a personal dossier · Table 2
Average draftAdvanced repair
Name-only join and buyer guessRetain narrow dated P01 status. P02 concerns WE-C02; reject its target-attendance join without asserting that target attendance is impossible. P08 is excluded metadata and unnecessary; omit it without inventing the missing detail. Operating site, buyer identity and interest remain unknown. Name removal does not establish anonymity. Correct company identity does not authorise contact.
text · 6 lines
ROLE: Adult internal account-evidence editor.
CONSTRAINTS: Synthetic Pipit packet only; no actual person, company, collection, upload, model or outreach.
PROCESS: Name each assertion subject. Bind supplied IDs without guessing aliases. Separate entity, site, role and person. Test the proposed relationship. Justify retained fields and omit unnecessary categories. Record unresolved joins and buyer unknowns.
VERIFICATION: Compare exact admitted passage, entity, time, qualifier and origin; preserve absent material and rejected wording.
OUTPUT SCHEMA: assertion; entity; card; origin; publication; retrieval; asOf; supportedScope; unknown; omission; nextCheck; actualUse=NOT_ASSESSED.
FAILURE MODES: Hold unread bodies, wrong subjects, unsupported budgets/quotes and external action. Paper prompt only; not executed.

Resolve the company without building a personal dossier

Supported work continues with the gap retained separately.

Source assertionEntity matchRelevant company fieldRetained company factMinimal briefUnresolved join/detail
Steps
  • Source assertion: Internal paper question
  • Entity match: Scoped check
  • Relevant company field: Supplied synthetic record
  • Retained company fact: Explain the decision
  • Minimal brief: No actual approval
  • Unresolved join/detail: Retain the unresolved branch
Relationships and conditions
  1. Source assertion to Entity match: check then continue
  2. Entity match to Relevant company field: check then continue
  3. Relevant company field to Retained company fact: check then continue
  4. Retained company fact to Minimal brief: check then continue
  5. Relevant company field to Unresolved join/detail: unresolved: retain gap

Diagram text equivalent: Source assertion then Entity match then Relevant company field then Retained company fact then Minimal brief. From Relevant company field, retain the separate Unresolved join/detail branch. It is not a supported fact or an actual-use approval.

Try it yourself · Activity 03

25-35 minutes, unpiloted estimate

Repair a contaminated company table

Repair: P01 makes WR-C01 active; P02 proves target attendance; P08 identifies a likely interested buyer. Explain retained fields and omitted categories.

  1. Name each assertion subject.
  2. Bind supplied IDs without guessing aliases.
  3. Separate entity, site, role and person.
  4. Test the proposed relationship.
  5. Justify retained fields and omit unnecessary categories.
  6. Record unresolved joins and buyer unknowns.

Identify the assumption that changed your conclusion and one unresolved check. How would you keep that gap visible when sharing the brief?

Worked answer

Retain narrow dated P01 status. P02 concerns WE-C02; reject its target-attendance join without asserting that target attendance is impossible. P08 is excluded metadata and unnecessary; omit it without inventing the missing detail. Operating site, buyer identity and interest remain unknown. Name removal does not establish anonymity. Correct company identity does not authorise contact.

Try it yourself · Activity 04

15-20 minutes, unpiloted estimate

Six answered chapter review questions

Attempt before reading answers. 1. Why is a shared name fragment insufficient? 2. What does registered office not prove? 3. Can information without a name identify someone? 4. Must every fact on a page containing a person be deleted? 5. Why omit P08 without inventing its contents? 6. Does an entity match permit outreach?

  1. Answer each question with a reason.
  2. Compare your reason with the supplied record and chapter method.

Which answer did your initial wording widen beyond the evidence?

Worked answer

1. It does not establish that WR-C01 and WE-C02 are the same entity or related. 2. An operating warehouse, site attendance or a place to visit. 3. Yes. Role and context may identify a person, so name removal alone is not anonymity. 4. No. Assess fields and retain necessary company evidence while considering irrelevant individual detail separately. 5. Its body is absent, it is excluded and its category does not answer the project question. 6. No. Actual subscriber classification, personal-data decisions and channel review remain separate.

Apply what you learned · Practice

Which handling of P02 is justified?

Explanation and answer

Keep P02 separate because it concerns WE-C02. The distinct entity ID prevents an unsupported target join.

Practice only. This does not count towards your exam or certificate.

Pipit diagram separating P02 wrong-entity evidence, P03/P04 shared announcement origin, and the dated P05 cancellation. The full explanation accompanies the figure.
Original illustration of fictional Pipit evidence: similar names, repeated sources and recorded dates need separate checks.

Text equivalent: Three separate checks use the fictional source packet. Entity: P02 describes WE-C02 and supplies no link to target WR-C01, so B02 is WRONG_ENTITY. Origin: P03 and P04 share expansion-announcement; the later article repeats the announcement and adds no independent confirmation. P05 has the separate cancellation-update origin. Time: the proposal is recorded on 3 May 2026, the repeating article is published on 4 May, and the proposal is cancelled on 20 May. At the fixed 1 October 2026 as-of, preserve that history rather than state that a site is opening now. Entity, origin and time must each be checked; none replaces the others.

Next chapter

Learn in your browser · Chapter 03 of 08

03

Make time visible instead of calling everything current

Separate history, later updates and present unknowns at a stated decision date.

Time determines what an account statement means. A proposal recorded in May, a vacancy advertised in June and a status card dated September cannot all be called current because they are read together in October. Pipit's paper brief uses 1 October 2026 as its decision date. This is a teaching frame, not an actual account assessment. Ask what each supplied statement supports at that date and what remains unknown. An old record can still be useful history. Its age does not automatically make its contents false, but its original meaning must not silently become a claim about present activity or buying intent.

Record four distinct times: the event date if supplied, publication date, retrieval date and brief as-of date. Publication describes when wording appeared; retrieval describes when it was accessed; event time concerns what happened or was proposed; as-of fixes the decision context. Do not invent an event date when only publication is known. A recently retrieved archive is not a recent event. An older document may contain an enduring identity fact, subject to appropriate later checks. The worksheet needs these distinctions before a drafter chooses present tense. A single freshness field cannot explain which of these times actually controls the proposed account claim.

P03 is a company announcement dated 3 May describing a proposed second container-handling site subject to review. P05 is a company update dated 20 May cancelling that proposal. The later card changes the supported interpretation of this particular proposal. It does not establish that the company will never expand or has abandoned every possible project. An average brief says the target is opening a second site and cites P03. The advanced version says the supplied May proposal was later cancelled, cites both cards and leaves other current plans unknown. This changes the actual proposition, rather than attaching a vague out-of-date warning to an attractive present-tense claim.

P04 is a 4 May trade article mirroring and crediting the expansion announcement. Its later publication does not reverse P05 or establish independent confirmation. Time and origin reasoning cooperate: P03 and P04 share a story, while P05 supplies a later cancellation. Sorting by retrieval alone can place the three together and hide their sequence. Write the relationship as proposal, dependent report and later cancellation. The reader should not have to infer whether two favourable-looking pages outweigh an update. Counting pages is the wrong operation for determining what happened to this specific proposed plan. Relevant later evidence matters because it addresses the same proposition.

P06 is an archived coordinator vacancy card dated 8 June. It records a supplied advertisement in that context; it does not establish a currently open vacancy, an actual named employee, procurement authority, budget or buying programme. Hiring language tempts a salesperson to infer pressure and urgency. Preserve such an idea as a research question requiring evidence rather than an established pain point. There are no actual applicants or employees here. Fresh retrieval would not refresh the vacancy's status. Rewriting the advertisement in present tense would conceal the uncertainty that the brief should expose. A useful next check names current vacancy status rather than guessing why a role was advertised.

P11 revision 1 is an invitation-to-quote card published 10 June with a stated 24 June deadline. At the 1 October paper date, that deadline is past. This supports a historical notice description, not a current invitation to submit or proof that no later procurement exists. Current status, budget and supplier selection remain unknown in this version. Do not derive a lost-sale story from the passed deadline. A next check would seek an admitted later update about the same notice while retaining entity and use boundaries. The paper task does not perform that search, contact an organisation or submit a quote.

The frozen lab defines P11 revision 2 as a fictional withdrawal dated 28 September, within the fixed October decision date. It retains the original June notice and deadline in history. Its supplied wording concerns withdrawal of that notice, not the end of every possible procurement activity. A revision worksheet should therefore retain both history and the scoped later change. Do not merely overwrite the original publication date and lose the relationship. The contract invalidates current brief and assumed review readiness after its permitted source change; rebuilding and fresh review are needed. That is defined finite behaviour, not an observed run in this chapter or actual procurement authority.

There is no universal thirty-day freshness rule here. Review need depends on the claim, decision, source and consequences of error. A corporate identity detail, live deadline and cancellation-sensitive project assertion have different change risks. Explain why a check is necessary rather than inventing a regulator-approved expiry interval. If an organisation later sets a review interval, record it as that organisation's scoped control and assess its suitability. These fictional dates support one timeline; they are not operational retention instructions or a prediction of how often a real account changes. Freshness and retention also answer different questions, so deleting material and refreshing a claim are not interchangeable decisions.

The group ico-personal-data-quality helps distinguish accurate status from misleading reuse. Historic records and opinions should be identifiable as such; presenting a past fact as current can mislead even when the original record was accurate in its period. This application does not imply that every company-only date error is a UK GDPR breach. Actual personal-data applicability and responsibilities require their own assessment. A repaired date does not establish lawful basis or contact permission either. Temporal accuracy is one component of an inspectable brief. Teach that component carefully without turning a regulator principle into a universal legal clearance or an unsupported finding about this fictional company.

Use a timeline method beginning with the intended decision date. Record supplied times for every assertion and keep missing fields absent. Locate later material about the same entity and proposition. Decide whether the earlier statement is supported as history, superseded in this packet or still unresolved in its present status. Choose wording preserving that status. Then identify evidence needed for a present claim. This prevents selecting the latest publication regardless of relevance and retaining older favourable material for a stronger sales story. The method does not ask which record makes the account look promising; it asks which proposition the records actually support.

Three worked repairs demonstrate the method. Opening a site becomes a May proposal later cancelled by the May update. Hiring now becomes a June archived advertisement with present vacancy status unknown. Accepting quotes becomes a June notice with a passed stated deadline and current procurement unknown in revision 1. Preserve original and repaired wording, with dates and reasons for the change. No repair asserts a current budget, chosen supplier or buying preference. A reviewer can then tell whether tense errors were resolved rather than hidden behind a caveat. Retaining rejected wording serves audit and learning purposes; it must not reappear as a factual headline in the brief.

A brief dated 1 October should remain readable on 2 October without silently becoming a current claim for that new date. Its as-of label and next checks travel with it. In the finite contract, a source revision, stop or handled rejection can invalidate current readiness, while previously returned copies remain historical copies outside the lab. Python argument-binding errors before a method body do not run its invalidation logic, and a fresh process resets local state. These are limits, not monitoring or security assurances. The stable paper principle is to reconsider dependent claims and review when relevant evidence changes, while retaining useful history and unestablished present buying intent.

  1. Set the decision as-of date.
  2. Record event, publication and retrieval times.
  3. Find later material on the same proposition.
  4. Classify history, superseded proposal and present unknowns.
  5. Repair tense and preserve qualifiers.
  6. Name next evidence without claiming it was obtained.

Scroll sideways to see every column.

Make time visible instead of calling everything current · Table 3
Average draftAdvanced repair
Everything called currentThe May proposal P03 was cancelled by P05. P06 is an archived June advertisement, not current vacancy evidence. P11 revision 1 describes a June notice with past deadline; current procurement is unknown. Revision 2 retains June history and adds withdrawal on 28 September, not cancellation of all procurement. Distinguish event, publication, retrieval and as-of times. Reconsider dependent wording and review after relevant change.
text · 6 lines
ROLE: Adult internal account-evidence editor.
CONSTRAINTS: Synthetic Pipit packet only; no actual person, company, collection, upload, model or outreach.
PROCESS: Set the decision as-of date. Record event, publication and retrieval times. Find later material on the same proposition. Classify history, superseded proposal and present unknowns. Repair tense and preserve qualifiers. Name next evidence without claiming it was obtained.
VERIFICATION: Compare exact admitted passage, entity, time, qualifier and origin; preserve absent material and rejected wording.
OUTPUT SCHEMA: assertion; entity; card; origin; publication; retrieval; asOf; supportedScope; unknown; omission; nextCheck; actualUse=NOT_ASSESSED.
FAILURE MODES: Hold unread bodies, wrong subjects, unsupported budgets/quotes and external action. Paper prompt only; not executed.

Make time visible instead of calling everything current

Supported work continues with the gap retained separately.

Decision dateSupplied timesLater relevant evidenceRepair status and tenseDated briefPresent status unknown
Steps
  • Decision date: Internal paper question
  • Supplied times: Scoped check
  • Later relevant evidence: Supplied synthetic record
  • Repair status and tense: Explain the decision
  • Dated brief: No actual approval
  • Present status unknown: Retain the unresolved branch
Relationships and conditions
  1. Decision date to Supplied times: check then continue
  2. Supplied times to Later relevant evidence: check then continue
  3. Later relevant evidence to Repair status and tense: check then continue
  4. Repair status and tense to Dated brief: check then continue
  5. Later relevant evidence to Present status unknown: unresolved: retain gap

Diagram text equivalent: Decision date then Supplied times then Later relevant evidence then Repair status and tense then Dated brief. From Later relevant evidence, retain the separate Present status unknown branch. It is not a supported fact or an actual-use approval.

Try it yourself · Activity 05

25-35 minutes, unpiloted estimate

Repair three current-tense assertions

As of 1 October, repair: opening a site; hiring now; accepting quotes. Build a chronology with P03, P05, P06 and P11 revision 1; then explain the supplied P11 revision 2 change.

  1. Set the decision as-of date.
  2. Record event, publication and retrieval times.
  3. Find later material on the same proposition.
  4. Classify history, superseded proposal and present unknowns.
  5. Repair tense and preserve qualifiers.
  6. Name next evidence without claiming it was obtained.

Identify the assumption that changed your conclusion and one unresolved check. How would you keep that gap visible when sharing the brief?

Worked answer

Filled chronology for the paper as-of date 1 October 2026. Every date below is supplied teaching metadata or notice content, not an actual event observation. The fixture retrieval date for P03, P05, P06 and P11 is 1 October 2026; retrieval is recorded separately from publication and events. 3 May 2026 | P03 revision 1 | eventDate 3 May; publicationDate 3 May; retrievedAt 1 October; asOf 1 October | second-site proposal subject to review, not an opened site or approved budget. 20 May 2026 | P05 revision 1 | eventDate 20 May; publicationDate 20 May; retrievedAt 1 October; asOf 1 October | cancellation of the particular 3 May proposal, not every later expansion plan. 8 June 2026 | P06 revision 1 | eventDate 8 June; publicationDate 8 June; retrievedAt 1 October; asOf 1 October | archived coordinator advertisement; current vacancy, purchase intent and budget unknown. 10 June 2026 | P11 revision 1 | eventDate 10 June; publicationDate 10 June; retrievedAt 1 October; asOf 1 October | original invitation-to-quote notice, with no current procurement status, approved budget or selected supplier supplied. 24 June 2026 | deadline stated in P11 body | not a separately supplied publication or independently observed closure event; the containing notice has event/publication dates 10 June and retrieval 1 October | the stated deadline is past at the as-of date. Current or later procurement remains unknown; do not infer that none exists. 28 September 2026 | permitted P11 revision 2 | eventDate 28 September; publicationDate 28 September; retrievedAt remains 1 October; asOf remains 1 October | the supplied update withdraws that invitation, while retaining the original 10 June notice and 24 June stated deadline as history. Other current procurement remains unknown. Repairs: opening a site becomes the May proposal later cancelled by P05; hiring now becomes the archived advertisement with present status unknown; accepting quotes becomes a historical notice with present status unknown in revision 1, then a scoped withdrawn notice in revision 2. Preserve original and repaired wording with these reasons. Defined contract recovery, not an executed run: change_source_revision(P11, 2) removes B08 bindings/classification and invalidates current brief, assumed review and export readiness; unaffected classifications remain. If stopped, explicitly resume_workflow(2), which does not restore review. Reopen/check current P11 revision 2, refresh B08 entity/origin/timeline and supplied W08_WITHDRAWN wording, rebuild the current brief and obtain fresh assumed internal review before a current export. Old returned copies remain historical outside the lab; a fresh instance/process resets local state. No real company, procurement, reviewer authority, permission or professional approval is established.

Try it yourself · Activity 06

15-20 minutes, unpiloted estimate

Six answered chapter review questions

Attempt before reading answers. 1. Which four times differ? 2. What does P05 change? 3. Does retrieval refresh P06 vacancy status? 4. What follows the passed P11 deadline? 5. What does revision 2 add? 6. Why retain original and repaired wording?

  1. Answer each question with a reason.
  2. Compare your reason with the supplied record and chapter method.

Which answer did your initial wording widen beyond the evidence?

Worked answer

1. Supplied event, publication, retrieval and decision as-of dates; missing fields stay absent. 2. It cancels this supplied P03 proposal, not every possible future expansion. 3. No. Access time does not establish a currently open vacancy or buying programme. 4. The notice is historical at the decision date; present procurement needs later evidence. 5. Withdrawal of that notice on 28 September while retaining June history, not a universal procurement conclusion. 6. It makes the actual time/status repair inspectable rather than hiding it behind a caveat.

Apply what you learned · Practice

Which time interpretation is supported?

Explanation and answer

P05 cancels the supplied proposal, not all future plans. The later update concerns a particular proposal, not every possible project.

Practice only. This does not count towards your exam or certificate.

Next chapter

Learn in your browser · Chapter 04 of 08

04

Trace evidence origins and test the actual sentence

Check exact propositions and origins instead of counting citations.

Corroboration begins with the origin of a proposition, not the number of pages carrying it. P03 is the fictional company expansion announcement and P04 is a trade article mirroring and crediting it. Two card identifiers preserve two records, but they supply one expansion-announcement origin. Saying two sources independently confirm the site changes the evidence relationship. Describe that dependence and inspect P05's later cancellation rather than gathering more copies of the same wording. This chapter teaches a small origin graph and exact passage comparison on synthetic material. It does not authenticate real publishers or establish the truth of arbitrary statements about a company.

Split a sentence into propositions before checking it. Westhaven is opening a second site, has a GBP150000 budget and prefers Pipit contains three material assertions, each needing its own subject, time and locator. A source supporting a proposal does not automatically support a budget or supplier preference in the same sentence. A paragraph footnote cannot make every clause supported. Preserve original wording, separate its claims and test each against the permitted extract. This makes the correction visible to an account manager who might otherwise treat a fluent sentence as one company fact. A polished summary should not be allowed to hide several different evidence gaps.

An origin graph records which source introduces a proposition and which records repeat or report it. In this packet the origin expansion-announcement links P03 and P04; cancellation-update belongs to P05. Overall there are two company origin families in that three-card set, but only one for the expansion announcement. These are teaching labels rather than authenticated publisher identities. They expose dependence so learners can reason about it. Actual origin labels would need their own traceable evidence. An original source can also be mistaken; independence alone is not a truth verdict. The graph answers where the account came from, while passage and conflict checks answer what it supports.

P07 is a deliberately faulty authored draft. Its GBP150,000 budget and buyer-preference quotation are absent from admitted evidence. They are not observed AI outputs, discovered fraud or actual buyer statements. Record the additions as unsupported and retain their original wording in the challenge record. A missing budget record means budget unknown here, not zero budget. A missing quotation means the words cannot be attributed to a buyer, not that the buyer holds the opposite preference. These distinctions prevent a correction from creating a second invented fact while trying to remove the first. P07's existence cannot verify its own unsupported additions.

Quotation is a strong attribution claim. Quotation marks imply supplied words from a source or speaker, with context that can be checked. Removing quotation marks from P07's invented preference does not repair it: buyer preference still appears established without evidence. Calling it anonymous feedback is no better because that invents a participant. The paper repair says no admitted source supplies the quotation and leaves preference and decision authority unknown. A later actual quotation would need provenance, context, relevant rights and personal-data review. This workshop supplies no actual person, interview or permission to publish an endorsement. The learner should retain an explicit rejection rather than write a softer attributed claim.

The group nist-genai-evidence describes confidently false content and verification concerns in voluntary US technical guidance. It explains why language quality and citation appearance do not replace checking a passage. A plausible title can be unrelated, a genuine citation can concern another entity, and a copied article can add no independent origin. No model is run here, and no failure rate is measured. The faulty draft is an original teaching challenge whose known defects permit a reasoned comparison. Describe the exercise accurately: it checks supplied propositions and explanations rather than demonstrating that a general-purpose assistant always identifies invented intelligence.

Check entity, period, qualifier and scope in the passage. P03 proposes a site subject to review; saying it is opening drops the condition. P05 cancels that proposal; saying every expansion has ended widens its scope. P02 concerns WE-C02; changing the subject makes an otherwise accurately copied schedule misleading. P11 revision 1 records a past deadline; a present invitation widens its time scope. Finding similar words is therefore insufficient. Supported wording preserves the distinctions controlling the source's meaning. An exact quotation can also mislead when the brief suppresses surrounding qualification or attaches it to the wrong time and company.

An average table gives the expansion story ninety per cent confidence because P03 and P04 agree, then places P07's budget in a polished summary. The advanced table records one shared announcement origin, P05's separate cancellation, the original proposal qualifier and the unsupported budget and quotation. It need not contain a confidence percentage. Its strength is an inspectable explanation. A score cannot repair a missing passage or quantify a buyer's intent from this packet. Describe uncertainty about a specific proposition and the evidence still needed. Do not turn the account as a whole into a pseudo-measured opportunity score on the basis of these authored cards.

Use a six-step assertion check. Preserve proposed wording. Divide it into material propositions. Bind each to the supplied entity and period. Locate the permitted passage and record origin dependence. Compare qualifiers and later conflicts. Choose narrower supported wording, a labelled hypothesis or an explicit unknown. Some sentences should be rejected from the factual brief altogether. A question remains useful without being a finding. This prevents laundering an invented budget into probably a large budget and treating the softened phrase as a reason to approach the account. The repair must remove unsupported meaning, not merely make it harder to challenge.

An advanced worked statement says the supplied 3 May announcement proposed a second site subject to review; the dependent 4 May article repeats it, and the 20 May update cancels that proposal. Other current plans are unknown in this packet. Compare Westhaven is expanding, confirmed by two sources. The shorter sentence loses proposal, dependence and cancellation. Good editing can shorten the advanced version while preserving those controlling relationships. Concision is valuable only when the resulting statement still means what the evidence supports. The account manager should not need access to a hidden correction log to discover that the brief's central opportunity statement has been superseded.

Rights and content checks remain separate. P09's unread restricted-report title establishes neither a report finding nor permission to obtain or copy its contents. P04's permitted synthetic extract can be compared with P03 without authorising commercial reuse of actual trade articles. The group ipo-account-source-rights supplies bounded UK guidance, and operative-text retrieval gaps are retained in the source register. This course does not certify licences or resolve every contractual restriction. An unsupported-content rejection and a rights hold give different next tasks. Combining them into one vague source problem makes it harder for the responsible person to identify what must actually be resolved.

Finish with a claim-to-passage matrix and dependency graph rather than a persuasive narrative. A colleague should see subject, period, locator, origin, qualifier and gap for each assertion. Retain P07's rejected additions as challenges, not findings. Keep the shared P03/P04 origin beside P05's separate cancellation. The frozen propose_statement method accepts code-owned wording after finite checks, not arbitrary semantic truth. This paper exercise claims no execution of that method. Its result supports discussion of the supplied packet, not account verification, legal approval or outreach release. Later chapters can build a minimal brief from these distinctions, provided they do not erase the unknowns making it honest.

  1. Preserve original wording.
  2. Split material propositions.
  3. Bind entity and period.
  4. Locate passage and shared origin.
  5. Compare qualification and later conflict.
  6. Separate supported wording, hypothesis and unknown.

Scroll sideways to see every column.

Trace evidence origins and test the actual sentence · Table 4
Average draftAdvanced repair
Two links treated as independent proofP03/P04 share one announcement origin, while P05 has a separate cancellation origin. The proposal is not established as currently opening. P07 supplies no admitted budget or verified buyer quotation; budget and preference remain unknown. Missing is not zero or opposite. Removing quotation marks does not create evidence. Preserve rejected wording and proposition-specific reasons.
text · 6 lines
ROLE: Adult internal account-evidence editor.
CONSTRAINTS: Synthetic Pipit packet only; no actual person, company, collection, upload, model or outreach.
PROCESS: Preserve original wording. Split material propositions. Bind entity and period. Locate passage and shared origin. Compare qualification and later conflict. Separate supported wording, hypothesis and unknown.
VERIFICATION: Compare exact admitted passage, entity, time, qualifier and origin; preserve absent material and rejected wording.
OUTPUT SCHEMA: assertion; entity; card; origin; publication; retrieval; asOf; supportedScope; unknown; omission; nextCheck; actualUse=NOT_ASSESSED.
FAILURE MODES: Hold unread bodies, wrong subjects, unsupported budgets/quotes and external action. Paper prompt only; not executed.

Trace evidence origins and test the actual sentence

Supported work continues with the gap retained separately.

Original assertionSplit propositionsPassage and originQualifiers and conflictNarrow statementUnsupported gap
Steps
  • Original assertion: Internal paper question
  • Split propositions: Scoped check
  • Passage and origin: Supplied synthetic record
  • Qualifiers and conflict: Explain the decision
  • Narrow statement: No actual approval
  • Unsupported gap: Retain the unresolved branch
Relationships and conditions
  1. Original assertion to Split propositions: check then continue
  2. Split propositions to Passage and origin: check then continue
  3. Passage and origin to Qualifiers and conflict: check then continue
  4. Qualifiers and conflict to Narrow statement: check then continue
  5. Passage and origin to Unsupported gap: unresolved: retain gap

Diagram text equivalent: Original assertion then Split propositions then Passage and origin then Qualifiers and conflict then Narrow statement. From Passage and origin, retain the separate Unsupported gap branch. It is not a supported fact or an actual-use approval.

Try it yourself · Activity 07

25-35 minutes, unpiloted estimate

Challenge a two-source claim and invented additions

Split: Westhaven is opening a second site, has a GBP150000 budget and prefers Pipit. Draw P03/P04/P05 origin relationships and test every proposition.

  1. Preserve original wording.
  2. Split material propositions.
  3. Bind entity and period.
  4. Locate passage and shared origin.
  5. Compare qualification and later conflict.
  6. Separate supported wording, hypothesis and unknown.

Identify the assumption that changed your conclusion and one unresolved check. How would you keep that gap visible when sharing the brief?

Worked answer

P03/P04 share one announcement origin, while P05 has a separate cancellation origin. The proposal is not established as currently opening. P07 supplies no admitted budget or verified buyer quotation; budget and preference remain unknown. Missing is not zero or opposite. Removing quotation marks does not create evidence. Preserve rejected wording and proposition-specific reasons.

Try it yourself · Activity 08

15-20 minutes, unpiloted estimate

Six answered chapter review questions

Attempt before reading answers. 1. How many expansion origins do P03/P04 supply? 2. Why split propositions? 3. Does an absent budget mean zero? 4. Does removing quotation marks fix P07? 5. What must passage checks preserve? 6. Does a confidence percentage replace a passage?

  1. Answer each question with a reason.
  2. Compare your reason with the supplied record and chapter method.

Which answer did your initial wording widen beyond the evidence?

Worked answer

1. One shared announcement origin, with two records retained; P05 is a separate cancellation origin. 2. Support for one clause does not establish budget or preference added beside it. 3. No. Budget remains unknown in admitted evidence. 4. No. Buyer preference remains unsupported without a source or actual speaker. 5. Entity, time, qualifier, scope, origin dependence and relevant later conflict. 6. No. Explain proposition-specific support and gaps instead of invented quantitative certainty.

Apply what you learned · Practice

Which repair respects origin and support?

Explanation and answer

Preserve dependence and check P05 cancellation. Records sharing an origin are not independent confirmation, and later cancellation matters.

Practice only. This does not count towards your exam or certificate.

Next chapter

Learn in your browser · Chapter 05 of 08

05

Keep hypotheses useful and unknowns honest

Turn a company observation into a testable research question while preserving missing evidence and permitted-data boundaries.

An account hypothesis is useful when it connects a recorded event to a question that could change the team's next research decision. It becomes invented intelligence when that connection is presented as knowledge of a buyer's intentions. Pipit's P06 card records an archived coordinator advertisement dated 8 June. It does not supply a current vacancy, a container order or a person's dissatisfaction. Keep the evidence object and the commercial question in separate sentences. That separation lets a colleague disagree with the inference while retaining the correctly described advertisement.

The distinction matters because sales briefs travel. A cautious note may become a slide, a CRM field or a manager's verbal account summary. If the qualification disappears, a tentative possibility can harden into an accepted fact without any new evidence. A writer should therefore make the status part of the sentence, rather than leaving it in a footnote. Describe what was recorded, identify the possible interpretation, and state what is unknown. A compact brief needs these boundaries more than a long research notebook because later readers see fewer surrounding details.

Treat budget, buying timetable, decision authority and incumbent supplier as different gaps. The absence of an approved budget in the packet does not establish a budget of zero, and it does not authorise a plausible estimate. P07's GBP150,000 figure belongs to an original faulty teaching draft. There is no additional transaction behind that number. Removing its currency symbol, rounding it or calling it an indicative budget would preserve an unsupported addition. The useful correction identifies the missing budget evidence and leaves the account owner a specific verification task.

A hypothesis should have at least one serious alternative explanation. A coordinator vacancy could relate to routine replacement, administrative workload or an unrelated programme. The fixture does not establish which explanation is right. This does not require inventing a new corporate story; alternatives are possible explanations used to test how weak the inference is. State them as possibilities, not discoveries. If the account-planning decision would be the same under every plausible explanation, collecting more detail about the vacancy may not be necessary for that decision.

Choose the information purpose before expanding the packet. Pipit's question concerns evidenced company projects involving reusable transit containers. A personal-interest detail would not establish such a project, a budget or authority to buy. P08 therefore contains only an exclusion reason and no body. The exercise does not read private detail and then remove it. This design makes the boundary visible before access. In a real organisation, a useful field-purpose record would explain both what a proposed field helps decide and why a less intrusive source cannot answer the same question.

Company information and information about individuals can appear together in a document. A brief should not assume that a public company record makes every included field non-personal. Equally, deleting all company evidence because a source includes an individual can make the research inadequate. ico-personal-data-quality supplies the bounded distinction between relevant company facts and identifiable individual information. Use it to ask a field-level question, not to label the whole folder safe. A role or reference token is not by itself a finding that a real person cannot be identified.

Source admission also needs a rights boundary. P09's report title is visible, but its contents are absent and access remains unresolved. The title may tell the learner which missing document needs consideration; it cannot reveal what that document concludes. Do not ask an assistant to reconstruct a paid report from its title or to turn a citation into an imagined summary. ipo-account-source-rights describes limited copyright exceptions and an older guidance page cross-read with selected March 2026 policy reporting. Neither the report's existence nor those policy discussions supply a licence for commercial account research.

Personal-data purpose, source reuse and delivery through an AI provider are separate decisions. A document may be relevant to the brief while its copying or supplier use remains unresolved. An organisation needs to understand the actual proposed processing, retention, further use and recipients before sending real information to a service. Pipit does not perform that transfer or select a supplier. Record these unresolved decisions beside the information request, rather than treating a product setting or a promise in a prompt as proof that every downstream use has been controlled.

Do not choose a lawful basis by copying an old source sentence. The B2B guidance's six-basis wording remains in an under-review page, while ico-legitimate-interests records the updated March 2026 terminology. The important practical point is the purpose, necessity and balancing decision, not a memorised count. The lab keeps legalBasisSelected false. Its rejection of unnecessary personal detail is a teaching boundary, not a completed assessment of a real controller's basis. Actual applicability and source use need the responsible organisation and a jurisdiction-qualified reviewer.

Use a six-step hypothesis method. First write the observation with entity, date and source. Second state the tentative interpretation separately. Third name plausible alternatives and identify the decision the hypothesis could affect. Fourth list the evidence missing from each material addition. Fifth define an authorised verification route and a stop condition. Sixth decide whether the hypothesis belongs in the shared brief or only in the research backlog. The method should reduce untested additions, rather than reward the largest number of prospecting ideas.

An authorised verification route is not automatically a message to a buyer. The account owner might consult an approved company record, ask an internal colleague to check a permitted source, or conclude that no further investigation is justified. The course does not establish those permissions for an actual organisation. Write the proposed route as a decision still requiring the appropriate owner. If it would involve contact information, profiling or a new channel, carry that boundary forward explicitly. The next step must not quietly collect the personal detail that the current brief deliberately omits.

The finite B06 path rehearses this reasoning without general language analysis. After inspecting P06, bind B06 to WR-C01, trace P06's origin, and record its dated event at the fixed as-of date. W06 then preserves the advertisement and labels the process-review question as a hypothesis. VACANCY_PROVES_PURCHASE is held because it is outside the accepted fixture wording. The method does not recognise arbitrary inaccurate prose in a new report. A learner should explain the evidence limitation themselves, rather than infer a general truth-detection capability from that scripted rejection.

Keep the unknown register usable. Each entry should name the missing object, explain why it matters, and identify who would decide whether further work is appropriate. Avoid writing research needed against every line with no priority. Pipit's nine unknowns remain unresolved even when all nine classifications are complete. Completing a worksheet and resolving an account question are different accomplishments. A manager can use the finished internal draft to understand that budget and buyer preference are absent, instead of mistaking those blank fields for oversights that an assistant should fill.

Finally, challenge the proposed value of more information. If another detail would merely make an unsupported story sound personalised, omit it. If a missing company fact could materially change the planning decision, keep the gap visible and specify the evidence needed. nist-genai-evidence explains why confident generated reasoning or citations still require verification. Here no model is used: P07 is deliberately authored challenge material. The successful learner preserves useful company observations, rejects invented knowledge and can explain why the permission to collect or share more information has not been established.

Scroll sideways to see every column.

Keep hypotheses useful and unknowns honest · Table 5
Prompt componentRequired instruction
RoleInternal account-evidence editor for Pipit; no buyer-intent prediction.
ConstraintsSupplied fictional cards only. P08/P09 bodies remain absent; no real people, rights, basis or outreach permission.
ProcessSeparate P06 observation, tentative interpretation, alternative explanations and missing budget.
VerificationBind WR-C01, retain P06 dates/origin and explain why P07 does not support its budget.
Output schemaObservation; hypothesis; alternatives; unknown; proposed owner; authorised route still to review; stop condition.
Failure modesHold invented budget, personal motives, unread report conclusions and permission inferred from public availability.

Scroll sideways to see every column.

Keep hypotheses useful and unknowns honest · Table 6
Worked draftEvidence decision
Average: The coordinator advertisement means a live container programme has GBP150,000 available.This adds present status, purchase intent and budget. P06 provides none of them; P07's number is challenge material.
Elite: P06 records an 8 June coordinator advertisement. Could an authorised company-source check establish whether any container-related programme exists?Keep routine replacement and unrelated work as possible alternatives. Budget and present intent remain unknown; no contact is authorised.
Finite rehearsalInspect P06 revision1; bind_entity('B06','WR-C01'); trace_origin('B06',['P06']); record_timeline('B06',['P06'],'2026-10-01'); propose_statement('B06','W06') returns status BOUNDED_STATEMENT with disposition HYPOTHESIS_ONLY. The unsafe wording is VACANCY_PROVES_PURCHASE.

From advertisement to a bounded question

Preserve observation, alternatives and permission boundaries before further research.

Dated P06 observationAlternative explanationsLabelled hypothesisMissing evidenceOwner reviews next step
Steps
  • Dated P06 observation: Advertisement recorded; present vacancy and purchase intent absent.
  • Alternative explanations: Possible routine replacement or unrelated work; none established.
  • Labelled hypothesis: A process-review question, not a confirmed programme.
  • Missing evidence: Budget, current status and purchase intent stay unknown.
  • Owner reviews next step: Actual collection, source use and contact permission remain unresolved.
Relationships and conditions
  1. Dated P06 observation to Alternative explanations: Test the inference
  2. Alternative explanations to Labelled hypothesis: Retain possible interpretations
  3. Labelled hypothesis to Missing evidence: Expose missing support
  4. Missing evidence to Owner reviews next step: Review an appropriate route

Text equivalent: P06 supplies a dated advertisement. Consider alternative explanations before writing a labelled process-review hypothesis. Identify absent budget, current vacancy and purchase intent. An accountable owner must review any further source use or contact; the hypothesis itself supplies no permission.

Try it yourself · Activity 09

60 minutes

Repair a hypothesis and permitted-data request

A Pipit colleague proposes: use P06 to confirm a current container programme, take the GBP150,000 budget from P07, infer a buyer's personal motivation from P08 and summarise P09. Prepare a corrected hypothesis record and field-purpose decision using only supplied material.

  1. Success criteria: retain the dated P06 observation and separate a hypothesis from a confirmed project.
  2. Give at least two possible alternative explanations and identify budget, purchase intent and current vacancy as unknown.
  3. Reject P07 as budget support; explain that P08/P09 bodies are absent, with distinct relevance and access/rights reasons.
  4. State a proposed accountable verification route without collecting new details or claiming a real basis, licence or outreach permission.
  5. On paper or in the local lab, explain the B06 W06 path and why VACANCY_PROVES_PURCHASE is held.

Which requested detail would change a legitimate planning decision, and which only makes the account story sound more personalised?

Worked answer

P06 records the 8 June coordinator advertisement, not a current project. A process-review question can be retained as HYPOTHESIS_ONLY, with routine replacement and unrelated work as possible alternatives. No approved budget is supplied; the GBP150,000 figure in P07 is part of a faulty authored draft. P08's irrelevant personal body and P09's rights-restricted body are absent, so neither is read or summarised. Ask the accountable owner whether an approved company-source check is justified; leave that route and actual processing permission unresolved. B06 needs entity, origin and timeline checks before W06. The unsupported alternative is held rather than repaired into a confident fact.

Scroll sideways to see every column.

Keep hypotheses useful and unknowns honest · Table 7
Chapter review questionAnswer after your attempt
What does the coordinator advertisement establish, and what does it not?A dated advertisement in the synthetic card; it supplies no current vacancy, purchase intent or budget.
Why does a hypothesis need alternatives?Alternatives expose the weak link between observation and interpretation and prevent one possible story being treated as the only explanation.
Why is an absent budget different from a zero budget?The packet contains no budget evidence. Zero would itself be an unsupported numerical assertion.
Why are P08 and P09 separate exclusions?P08 is irrelevant personal detail; P09 has unresolved access and rights. Both bodies are absent, but the reasons differ.
Does W06 acceptance assess arbitrary prose or a real lawful basis?No. It selects a finite teaching wording after fixture checks; legalBasisSelected remains false.
How should the obsolete six-basis sentence be handled?Use the updated ico-legitimate-interests source for current terminology and retain the older B2B page's review caveat; do not select an actual basis from either shortcut.

Apply what you learned · Practice

A colleague proposes filling an absent budget with an industry estimate. What should Pipit's research record do?

Explanation and answer

Keep the approved budget unknown and specify what authorised evidence would be needed. An approved account budget is missing. Neither plausibility, zero nor an authored challenge supplies it.

Practice only. This does not count towards your exam or certificate.

Next chapter

Learn in your browser · Chapter 06 of 08

06

Share the minimum brief and retain control of its use

Use a six-component prompt and traceable output schema to preserve company evidence, hypotheses, unknowns and held actions.

A useful internal brief is a small information product with a defined reader and decision. Pipit's reader needs to understand what the supplied evidence says about WR-C01, which additions were rejected and what remains unresolved. The reader does not need a persuasive biography of a supposed buyer. Decide the brief's purpose before drafting its opening paragraph. Without that decision, an assistant may optimise for fluency, length or apparent commercial insight and remove the very qualifications that make the research useful to an accountable colleague.

Compression should preserve relationships, not merely shorten sentences. In B04, three cards contribute to the reasoning: P03's announcement, P04's dependent article and P05's cancellation. There are two company origin families overall, but only one announcement origin behind the supposed expansion confirmation. A summary that says three sources support expansion loses both dependence and contradiction. A summary that says there is no information loses the dated history. The faithful brief preserves the proposal, the repeated origin and the later cancellation with their different roles.

Prepare a field-purpose matrix before choosing the output format. Entity ID and as-of date help a reader identify the scope. Source bindings let a reviewer reopen the relevant card. Unknowns prevent a blank from becoming an invented fact. A large copied source body may be unnecessary when a precise statement and passage reference suffice. The matrix should explain retention as well as omission: a brief that drops the cancellation's source is smaller but less reliable. ico-personal-data-quality informs adequacy and minimisation of personal information; it does not certify this fictional brief's lawful use.

Separate the account's identity from the authority of the tools that handle the brief. WR-C01 is a code-owned target token. Binding an assertion to it does not verify a real company's identity. Likewise, a local file that can be opened is not proof that its contents are authentic, current or licensed for every purpose. The canonical fixture check detects departure from the supplied teaching dataset. A real workflow would need its own provenance and source-use decisions. Keep those questions visible rather than replacing them with a file-integrity success.

Bound a prompt with six components: role, constraints, process, verification, output schema and failure behaviour. Each component should change what the proposed assistant may produce or what the reviewer must inspect. A role establishes the task as internal evidence editing. Constraints identify admitted material and excluded uses. The process preserves entity, origin and time before summarisation. Verification demands direct support and unresolved gaps. The schema makes those gaps visible. Failure behaviour states what to do when evidence is missing instead of encouraging a confident completion.

These instructions are a paper proposal for an assistant task, not an enforceable supplier contract. Writing do not retain data in a prompt would not establish what an actual provider logs, stores or uses for further purposes. No external AI service participates in Pipit. Before any real information entered another tool, the responsible organisation would need to examine the actual service, recipients, settings, terms and controls. The course's prompt can make a review question explicit; it cannot answer an unexamined supplier question by assertion.

Choose a schema that resists the brief's most tempting errors. Include entity, as-of date, challenged assertion, narrow statement, source and revision, origin notes, event timeline, hypothesis label and unknown. Keep omissions and the outreach docket separate from the account summary. A single ready field could confuse completed research formatting with permission to contact someone. The correct internal packet can be complete as a teaching artefact while the budget, buyer preference and actual outreach permission remain missing. Do not replace that useful distinction with a general confidence score.

Pipit supplies that structure without asking a model to invent it. learner_materials() emits a blank nine-row assertionWorksheet, a briefTemplate and a visible finite wording menu. The command's worksheet output is not a completed answer and the template is not a saved appendix file. The four portable sources are README.md, account_cards.json, pipit_lab.py and test_pipit_lab.py. A learner can use the emitted materials on paper, then compare reasoning with the teacher walkthrough. This is an open teaching exercise, not a scored assessment whose answers are kept secret.

The checks have an order because each depends on evidence already inspected. list_source_cards() provides metadata, including P10's preference record. open_company_card(card_id, expected_revision) binds a readable body to its exact fixture revision. bind_entity establishes the target; trace_origin requires the prescribed inspected source set; record_timeline records the event sequence at the fixed as-of date. propose_statement then selects a supported finite wording. Jumping directly to a wording omits the checks that make the example reproducible, even if the selected sentence happens to be cautious.

Finite wording acceptance is a narrow implementation property. W01 through W09 correspond to nine code-owned corrections at source revision one. The lab does not parse a new account report, search the web or decide whether arbitrary prose is true. Wording IDs make the intended outputs inspectable and support meaningful negative checks. An unrecognised sentence is held because it is outside the finite contract, not because an AI discovered its error. Explain that distinction when presenting the exercise to colleagues who might otherwise mistake a controlled demonstration for general prospect-research capability.

build_internal_brief() requires all nine classifications and their current card bindings. It returns INTERNAL_DRAFT rather than an operational action. The output includes nine unresolved unknowns, two omitted-body reasons, an origin map, timelines and the held outreach docket. Those objects are part of the brief's meaning, not optional technical attachments. A reader who sees only the corrected statements may miss why further action remains unresolved. Preserve the unknowns and limits when converting the packet into a short narrative or an internal presentation.

Design verification around reconstructing the result. Another reader should be able to identify the source passage for a narrow fact, explain a wrong-entity rejection and distinguish an advertised role from an approved purchase. Ask the reader to challenge what would be lost if the brief were copied without its entity and date. nist-genai-evidence recommends source and citation verification within a voluntary framework; it provides no government approval for Pipit. Here direct checking means comparing original synthetic passages with the worksheet, not trusting a generated explanation of what it supposedly read.

An export is a transport event whose scope needs to be understood. export_internal_review_packet() returns a deep copy after the current assumed review; it writes zero files. The demo prints that packet to the terminal. A person who later saves or forwards it creates a copy outside the session, which the lab cannot recall. The packet should therefore retain its revision, as-of date and internal-only boundary. A real service would need an explicit correction and distribution process before anyone relied on automatic withdrawal of an earlier brief.

Assess the finished brief by fidelity and usefulness together. A statement can be faithful but irrelevant to the account question, or relevant but unsupported. Keep the shortest sufficient explanation of each material boundary and use a separate research backlog for work that has not been authorised. The elite result is a reader who can reconstruct the facts and see what has not been established. Attractive prose, a completed template and an accepted wording ID are supporting conveniences; they do not resolve real rights, processing decisions or a buyer's intentions.

Scroll sideways to see every column.

Share the minimum brief and retain control of its use · Table 8
Elite prompt componentPipit instruction
RoleInternal evidence editor for WR-C01; no autonomous prospecting or buyer profiling.
ConstraintsSupplied local cards only; no P08/P09 bodies, real people, outside sources, model execution or outreach.
ProcessInspect sources; bind entity; trace exact origins; order recorded events; choose narrow finite wording; minimise fields.
VerificationKeep P03/P04 dependence and P05 cancellation distinct. Show passage IDs, revisions and every missing budget or intent claim.
Output schemaEntity; asOf; challenged claim; narrow statement; sourceBindings; sourceOriginMap; eventTimelines; unknownRegister; omissions; outreachReviewDocket.
Failure behaviourHold missing, mismatched, uninspected or stale evidence. Retain an explicit unknown instead of filling it.

Scroll sideways to see every column.

Share the minimum brief and retain control of its use · Table 9
Worked briefing instructionInspectable result
Average: Write a confident short account summary; leave technical detail out.The instruction encourages removal of source dependence, cancellation, unknowns and the action boundary.
Elite: Summarise the company packet and preserve how each material sentence is supported.B04 uses P03/P04/P05: one announcement origin plus a separate cancellation update. W04 keeps those scopes visible; B05/W05 leaves budget missing.
Minimum output verificationA complete INTERNAL_DRAFT has nine classifications and nine unknowns. P08/P09 bodyPresent remains false; the docket records DO_NOT_CONTACT and NOT_ASSESSED.
Actual delivery limitThe prompt is a paper proposal. The implemented local lab selects finite wording IDs and uses no model; export returns a copy with filesWritten=0.

A brief that carries its evidence boundaries

A small narrative remains connected to its sources, gaps and held action docket.

Inspected source packetEntity, origin and timeMinimal internal draftUnknowns and omissionsCurrent assumed reviewReturned internal copy
Steps
  • Inspected source packet: Exact company card IDs/revisions and preference metadata.
  • Entity, origin and time: Verify scope before selecting finite wording.
  • Minimal internal draft: Nine classifications with necessary source bindings.
  • Unknowns and omissions: Budget missing; P08/P09 bodies absent.
  • Current assumed review: Fixture token only; real authority remains false.
  • Returned internal copy: No file write or outreach; retain revision and limits.
Relationships and conditions
  1. Inspected source packet to Entity, origin and time: Inspect exact evidence
  2. Entity, origin and time to Minimal internal draft: Select bounded wording
  3. Minimal internal draft to Unknowns and omissions: Carry unresolved fields
  4. Unknowns and omissions to Current assumed review: Review the complete scope
  5. Current assumed review to Returned internal copy: Return a labelled packet

Text equivalent: Inspect the supplied packet, then check entity, origin and recorded time. Build a minimal internal draft with source bindings, unknowns and omissions. A current assumed review permits only a returned internal copy. The copy retains its revision and held outreach boundary; no file or message is written by the lab.

Try it yourself · Activity 10

75 minutes

Design and verify a bounded account brief

A colleague wants a short persuasive account narrative with no source detail. Produce a six-component paper prompt, a field-purpose matrix and a minimum internal brief outline. Use B04 and B05 as verification examples, then explain what the actual local lab can and cannot do.

  1. Success criteria: include all six prompt components with instructions specific to Pipit's evidence and excluded uses.
  2. Retain entity, fixed as-of date, card revisions, origins, timelines, unknowns, omissions and the separate action docket.
  3. Describe B04 as one announcement origin plus a cancellation origin, not two independent expansion confirmations.
  4. Keep B05's approved budget unknown and reject P07 as verification of its own invented number.
  5. Explain emitted worksheet/template, four portable files, finite wording acceptance and the returned-copy export limit.

Which brief field would you keep even under a strict length limit because removing it would change the statement's meaning?

Worked answer

Completed six-component paper prompt (the concrete proposal in block14): Role: Internal evidence editor for WR-C01; no autonomous prospecting or buyer profiling. Constraints: Supplied local cards only; no P08/P09 bodies, real people, outside sources, model execution or outreach. Process: Inspect sources; bind entity; trace exact origins; order recorded events; choose narrow finite wording; minimise fields. Verification: Keep P03/P04 dependence and P05 cancellation distinct. Show passage IDs, revisions and every missing budget or intent claim. Output schema: Entity; asOf; challenged claim; narrow statement; sourceBindings; sourceOriginMap; eventTimelines; unknownRegister; omissions; outreachReviewDocket. Failure behaviour: Hold missing, mismatched, uninspected or stale evidence. Retain an explicit unknown instead of filling it.

Completed field-purpose matrix, read each row as field -> purpose -> decision: WR-C01 -> bind the account question -> retain; asOf 2026-10-01 -> bound present-status interpretation -> retain; assertion/card/revision IDs -> reconstruct support -> retain; P03/P04 shared origin and P05 cancellation -> distinguish dependence and contradiction -> retain; event dates -> preserve proposal/cancellation history -> retain; narrow statements and named unknowns -> prevent unsupported completion -> retain; approved budget -> absent from admitted evidence -> hold as unknown, never fill with P07's GBP150,000; P08 personal-interest body -> no justified company-project purpose -> omit, body absent; P09 report body -> unresolved access/rights -> hold unread, body absent; proposed provider retention and real source permission -> actual controls not examined -> hold for accountable review; P10 objection and UNKNOWN subscriber -> preserve action boundary -> retain in separate held docket, no recipient address.

Completed minimum brief outline: header entityId=WR-C01, asOf=2026-10-01, sourceRevision=1, internal-only draft. B04 entry: sourceBindings P03/P04/P05 each at1; sourceOriginMap expansion-announcement for P03/P04 and cancellation-update for P05; eventTimelines retain the 3 May proposal, P04's 3 May recorded event with 4 May publication, and 20 May cancellation; W04, disposition DEPENDENT_ORIGIN, with independent confirmation unresolved. B05 entry: P07 at1 identifies the faulty authored draft, event list empty; W05, disposition UNSUPPORTED; approved purchase budget unknown. Complete the other seven rows before building the actual aggregate: these two worked entries are an outline, not a build-ready nine-row packet. Add all nine unknownRegister entries, omissions P08/P09 and outreachReviewDocket with DO_NOT_CONTACT, prior objection=true, subscriberClass=UNKNOWN and realPermission=NOT_ASSESSED. Retain source/revision and briefRevision fields when reviewed.

learner_materials() emits a blank assertionWorksheet, briefTemplate and finite menu; the four portable files are README.md, account_cards.json, pipit_lab.py and test_pipit_lab.py. Inspect, bind, trace and record time before selecting wording. build_internal_brief() requires all nine current classifications; a current assumed review precedes export_internal_review_packet(), which returns a deep copy and writes zero files. These are paper expectations of the finite implementation, not an observed model result. The completed prompt does not establish supplier controls, source rights, a real basis, identity or outreach.

Scroll sideways to see every column.

Share the minimum brief and retain control of its use · Table 10
Chapter review questionAnswer after your attempt
Why is compression a provenance problem?Removing source scope, dependence or a later cancellation can change meaning even while shortening correct words.
How many expansion confirmation origins do P03/P04 provide?One. P05 is a different cancellation origin, so the complete B04 trace has two company origin families with different scopes.
What do six prompt components accomplish, and what do they not establish?They make a bounded task and verification visible; they do not establish actual provider retention, processing permission or legal authority.
Does learner_materials() complete the exercise?No. It emits blank worksheet rows, a template and a visible wording menu for open teaching practice.
What does finite wording acceptance establish?The code-owned wording and required fixture checks match; it is not general semantic or real-account truth verification.
What happens when a packet is exported?A current reviewed packet is returned as a copy, with zero files written. A separately saved copy is outside the lab and cannot be recalled.

Apply what you learned · Practice

A manager removes the unknown register from an otherwise correct Pipit brief. What needs correction?

Explanation and answer

The missing budget, intent and permission must remain visible in the shared artefact. The unknown register preserves material limits. A shared brief must retain those limits even if its format changes.

Practice only. This does not count towards your exam or certificate.

Next chapter

Learn in your browser · Chapter 07 of 08

07

Separate a researched account from an outreach decision

Review purpose, jurisdiction, channel and preferences separately from account evidence and an assumed internal review.

An internal account brief and an outreach decision answer different questions. The brief asks what the supplied company evidence supports and what remains unknown. Outreach review asks whether a particular proposed action, by a particular organisation, through a particular channel, is appropriate and permitted in its actual context. A correct account fact cannot answer that second question on its own. In Pipit, the internal packet remains useful precisely because it carries a held action docket instead of pretending that completion of company research supplies contact permission.

Review the intended purpose before collecting more information, not merely at the point of sending a message. ico-lead-generation treats collection, enrichment and profiling for direct marketing as relevant activities in their own right. A team cannot avoid that boundary by calling its work account research while constructing personal profiles for later targeting. State which organisational decision the company brief supports and whether any later use is proposed. If that purpose changes, the responsible organisation must review the change; an earlier source-admission decision is not a perpetual permission for new uses.

Make the jurisdiction and channel part of the action question. A UK electronic message, a live telephone call and a US commercial email do not share one interchangeable rule. Private social messages can fall within electronic-mail guidance, while a public post presents other questions. The course does not supply a complete review of every platform or territory. A reviewer should therefore identify the intended action in ordinary language, record the reviewed scope, and leave unreviewed markets visibly unresolved rather than applying one convenient source worldwide.

A company's legal form and a communication service's subscriber classification are related but different evidence objects. The UK guidance distinguishes corporate and individual subscribers, including relevant sole-trader and partnership differences. A target whose name ends in Ltd does not automatically settle every recipient, address or phone line in a proposed action. Pipit's R-T01 token has UNKNOWN subscriberClass, even though WR-C01 is a fictional limited-company target. Keep the missing classification visible. The fixture never investigates an actual bill, service holder or organisation to resolve it.

Do not infer that a generic address removes every obligation. ico-channel-review explains that PECR can apply without knowing a recipient's name, and a supposedly generic label may still identify a person in context. Likewise, a named work contact can be personal information even when the organisation has a separate legal personality. These distinctions matter when selecting the information to use and the channel to review. They are not an instruction to acquire additional identities. If the intended action needs unresolved evidence, the accountable owner should decide whether it is appropriate to proceed with review at all.

The corporate-subscriber treatment of electronic-mail consent is not a general declaration that a proposed message is lawful. Identity, opt-out and relevant personal-data questions remain separate. Individual-subscriber unsolicited messages have their own consent or applicable soft-opt-in conditions. The April 2026 ICO set also distinguishes the charitable-purpose soft opt-in from products and services. Pipit is a fictional container seller, not a charity campaign. Teach the scoped distinction without converting that fact pattern into a universal cold-email rule or a completed assessment of a real sales organisation.

Preferences and objections are not account-interest signals. A recorded wish not to receive marketing must not be outweighed by an attractive project, an enthusiastic manager or a speculative buying score. P10 supplies a synthetic priorMarketingObjection flag and no contact body. The code's docket is DO_NOT_CONTACT and realPermission is NOT_ASSESSED. This does not establish the authenticity or full legal scope of any real person's preference. It demonstrates that a finite workflow can preserve a stop boundary without requiring the learner to expose an address, phone number or biography.

Transparency must not be reduced to a link left somewhere in a footer. ico-notice-timing describes direct collection at obtaining and indirect collection within a reasonable period, subject to a one-month outer bound and earlier first-communication or first-disclosure limits where applicable. Its exceptions are source-dependent, and its DUAA review notice remains relevant. A brief that records a source does not itself fulfil an actual organisation's notice duties. No notice is sent in Pipit. Keep the real transparency decision separate from the fictional passage references used to teach provenance.

A lawful-basis review also needs its own accountable reasoning. ico-legitimate-interests supplies the updated purpose, necessity and balancing structure, distinct from recognised legitimate interest. It does not establish that all B2B research qualifies. If a channel requires consent, an attractive commercial purpose cannot simply replace it with another basis. The older B2B six-basis sentence must not be copied as current teaching. The course and code select no real basis; they train the learner to recognise the missing decision and its relation to the actual proposed purpose.

Use a six-step review docket. First define the proposed action and its jurisdiction. Second identify the channel and subscriber evidence still required. Third identify any personal information and its processing purpose. Fourth review the relevant basis, transparency, preferences and source-use questions with the responsible owners. Fifth record the decision, limits and evidence without inventing authority. Sixth keep a correction, objection and stop route that remains effective after the brief changes. A complete docket may record hold or not assessed; completeness is not the same as a positive permission decision.

Human review should be demonstrated through the ability to challenge a material boundary. Ask who can reject an unsupported budget, insist that a contact objection remains visible, or stop a proposed use of an unread report. A reviewer who merely approves the attractive prose has not examined the evidence problem. In a real organisation, the person's actual role, competence and authority would need to be established through that organisation's processes. A token in the lab is not evidence of identity, an employment role or a professional qualification.

Pipit makes that limitation explicit in record_review(reviewer_token, brief_revision, reason). The token pipit-assumed-reviewer and the reasons evidence_checked or manual_review are code-owned teaching assumptions. They create ASSUMED_INTERNAL_REVIEW only for the current brief revision, with actualReviewerVerified false. The review can permit a returned internal packet but cannot set outreachAuthorised true. request_external_action(action_id) holds external actions even after a successful internal review. It has no send, scrape, phone, CRM or model operation hidden behind the action label.

Treat the US guide as a contrast that prevents careless transfer of a UK shortcut. ftc-us-email-scope states that commercial email is not exempt merely because it is B2B and describes selected federal requirements. It is not a UK permission, a full US state-law review or an answer about calls, texts or every cross-border situation. If a future use includes US recipients, assign a separate actual applicability review. A responsible brief can say that this jurisdiction is unresolved without inventing a global rule or an approval from a voluntary technical framework.

The final review outcome should explain what remains usable. A colleague may use the fictional internal brief to understand the source history and research gaps, while the fixture's marketing action remains held. Retain the account owner's proposed next evidence task separately from any action involving a recipient. This makes it possible to disagree about further research without losing the objection record. A neat dossier, an assumed review stamp or a high knowledge-exam score must not be represented as a legal, professional or operational authorisation.

Scroll sideways to see every column.

Separate a researched account from an outreach decision · Table 11
Prompt componentPre-action review instruction
RoleOrganise an unresolved action-review docket; do not send or classify real recipients.
ConstraintsFictional R-T01 metadata only. Preserve prior objection, UNKNOWN subscriber class and NOT_ASSESSED real permission.
ProcessSeparate company facts, intended action, territory, channel, processing purpose and responsible review.
VerificationCheck what evidence is actually supplied for each decision; do not equate Ltd, public availability or an internal review with clearance.
Output schemaProposed action; jurisdiction; channel; missing classification; processing/notice/preferences questions; owner; hold reason; correction route.
Failure modesHold action on unresolved requirements. No inferred consent, six-basis shortcut, objection override or US-to-UK rule transfer.

Scroll sideways to see every column.

Separate a researched account from an outreach decision · Table 12
Worked reviewDecision and limit
Average: The account brief is checked, so send the container offer to R-T01.This merges source accuracy with contact authority and disregards P10's objection and missing subscriber evidence.
Elite: Preserve the internal company brief and keep a separate held action docket.P10 records priorMarketingObjection=true, subscriberClass=UNKNOWN and realPermission=NOT_ASSESSED. The fixtureAction remains DO_NOT_CONTACT.
Assumed review examplerecord_review('pipit-assumed-reviewer', current_brief_revision, 'evidence_checked') requires an existing current INTERNAL_DRAFT. It returns ASSUMED_INTERNAL_REVIEW, not authenticated identity.
After internal reviewexport_internal_review_packet() can return the labelled internal packet. request_external_action('email') remains HOLD(EXTERNAL_ACTION_EXCLUDED); no message is sent.

Two reviews with different questions

Company evidence and action permission cannot be collapsed into a single ready label.

Company evidence briefAssumed internal reviewProposed marketing actionAction-review docketExternal action heldInternal packet only
Steps
  • Company evidence brief: Dated facts, hypotheses and unknowns.
  • Assumed internal review: Current fixture brief checked; actual authority false.
  • Proposed marketing action: Channel, territory and processing purpose must be specified.
  • Action-review docket: P10 objection and UNKNOWN subscriber evidence.
  • External action held: DO_NOT_CONTACT; realPermission NOT_ASSESSED.
  • Internal packet only: A returned copy is not permission to contact.
Relationships and conditions
  1. Company evidence brief to Assumed internal review: Check company evidence
  2. Assumed internal review to Internal packet only: Return an internal copy
  3. Proposed marketing action to Action-review docket: Review separate requirements
  4. Action-review docket to External action held: Preserve objection and gaps
  5. Internal packet only to External action held: Do not infer contact permission

Text equivalent: Dated company evidence can proceed through the fixture's assumed internal review to an internal packet. A proposed marketing action follows a separate review of channel, territory, processing purpose and preferences. P10's objection and unknown subscriber evidence lead to a held external action. The internal packet cannot supply the missing contact permission.

Try it yourself · Activity 11

70 minutes

Write a held outreach-review docket

A sales manager treats a checked company brief as approval to contact R-T01. Prepare a review docket using P10 metadata, a UK electronic-mail proposal and a separate proposed US use. Identify what is supplied, what remains unresolved and what the lab's assumed review actually authorises.

  1. Success criteria: separate the company brief from the proposed marketing action and identify jurisdiction/channel.
  2. Retain prior objection, UNKNOWN subscriber class, DO_NOT_CONTACT and NOT_ASSESSED without inventing an address or identity.
  3. Describe basis, transparency, source use and preferences as separate actual review questions; preserve earlier timing limits and applicable exception review.
  4. Use the current legitimate-interests source rather than the outdated six-basis sentence; do not select a real basis.
  5. Explain the assumed review token and why all external actions remain held; keep the US federal guide separate from UK clearance.

Who in a real organisation would be able to stop the action, and what evidence would demonstrate that person's actual authority?

Worked answer

Keep the company brief as internal evidence and create a separate action docket. The fixture supplies a prior objection for R-T01, no contact body and UNKNOWN subscriber class. It therefore preserves DO_NOT_CONTACT and NOT_ASSESSED, with actual authority flags false. A UK proposal would require actual channel/subscriber, processing-purpose/basis, notice, preference and source-use review by responsible owners. Indirect notice timing is not merely one month: reasonable-period and earlier communication/disclosure limits and applicable exceptions matter. Use updated ico-legitimate-interests terminology; do not choose a basis in this exercise. The US proposal remains a separate applicability question, not a UK shortcut. The fixture token records ASSUMED_INTERNAL_REVIEW of a current internal draft. It cannot authorise sending; request_external_action holds the proposed action.

Scroll sideways to see every column.

Separate a researched account from an outreach decision · Table 13
Chapter review questionAnswer after your attempt
Why is a researched account different from an authorised contact?Evidence about a company does not settle channel, subscriber, privacy, preference or source-use requirements for a particular action.
Does a generic address remove PECR?No. The electronic-mail guidance applies without a named recipient; personal-data assessment is a separate contextual question.
What does P10 establish in the fixture?A synthetic prior objection and unknown subscriber class. DO_NOT_CONTACT and NOT_ASSESSED remain; no actual identity or consent is verified.
Why is a month-only indirect-notice rule incomplete?Reasonable-period and earlier first-communication/first-disclosure limits may apply, with source-dependent exceptions and the retained DUAA review caveat.
What is the authority of pipit-assumed-reviewer?Only a code-owned teaching assumption for a current internal brief. Actual reviewer identity and outreach authority remain false.
What does the US federal guide fail to settle?UK permission, every US state or channel, and actual cross-border applicability. Those require separate qualified review.

Apply what you learned · Practice

An assumed internal review is recorded for a complete Pipit brief. What follows for the R-T01 action?

Explanation and answer

The internal packet may be returned, while external action remains held. The code separates the current internal packet from the prior objection and unresolved real permission. Internal review supplies no outreach authority.

Practice only. This does not count towards your exam or certificate.

Next chapter

Learn in your browser · Chapter 08 of 08

08

Produce, challenge and recover a dated account brief

Build the full internal packet, then rehearse a controlled withdrawal and recovery without restoring stale review or losing historical limits.

A trustworthy account brief needs a correction route as well as an initial reasoning route. Someone may discover a later source, challenge a mistaken join or notice that an old copy is still circulating. The useful response is to preserve the original reasoning, invalidate current reliance where appropriate, and produce a clearly distinguished correction. Do not quietly edit the sentence and leave its review record untouched. Pipit's final capstone treats change as part of account research, rather than an exceptional technical problem that begins only after publication.

Keep source revision and brief revision separate. sourceRevision identifies the fixture's current source state: initially one, later two after the allowed P11 transition. A card has its own revision binding, so unchanged cards can remain at revision one. briefRevision is a monotonically increasing number assigned when a complete internal draft is built. It is not a timestamp, a score or evidence that a real account was reverified. A review must match the actual current brief object and hash, not merely a familiar revision number copied into a spreadsheet.

The permitted change is specific. change_source_revision('P11', 2) adds an originally authored update dated 28 September 2026 that withdraws the invitation. The original 10 June notice and stated 24 June closing date remain in its history and the revised wording. The fixture's as-of date stays 1 October. This is not a live discovery or a claim that today's date has advanced. It is a controlled source revision that teaches how a later recorded event changes what the earlier invitation can support.

Only B08's classifications and supporting checks are directly invalidated by that source change. The other eight correctly completed classifications remain in the session because their bound cards have not changed. Every current brief, assumed review and current export approval is cleared, since the combined packet includes B08. This distinction prevents two opposite errors: retaining an outdated aggregate approval, or pretending that every unaffected source has become false. A reviewer should identify the affected dependency while checking the complete revised packet before it is used internally again.

The revised B08 wording is W08_WITHDRAWN, with disposition WITHDRAWN_NOTICE. W08's original historical-notice wording is no longer the accepted selection for that case after revision two. Neither wording establishes every later procurement activity, an approved budget or a chosen supplier. The unknown register still carries current procurement status. A withdrawal of one invitation supplies a specific historical fact, not a general conclusion that the company has stopped buying containers. The corrected packet must preserve that narrow meaning.

Stopping is an explicit control that a person can exercise. stop_workflow accepts the bounded reasons learner_pause, error_found, source_change or review_required. Normal work and export are held while stopped. The P11 fixture change is allowed during that pause so the source state can be updated before work resumes. resume_workflow requires the exact current dataset revision and a stopped session. It returns reviewRestored false. Resumption permits the necessary work to continue; it does not recreate the review that the stop or source change removed.

Recovery therefore needs evidence work rather than another ready label. Inspect P11 revision two, bind B08 to WR-C01, trace its source, record the updated event at the fixed as-of date and choose W08_WITHDRAWN. Then build a new complete brief, obtain a current assumed review and return a new internal packet. If the learner first tries an obsolete wording and receives a handled rejection, that assertion's checks are cleared. Rebind, retrace and retime before retrying the permitted wording. A successful correction is a visible sequence, not an automatic consequence of clicking resume.

Understand what a failed call actually did. Handled method-body rejections invalidate the current brief, review and current export readiness. Python argument-binding errors are different: an unexpected keyword or missing required argument raises TypeError before the method body starts. The previous state remains because invalidation did not run. A later valid export still performs its integrity and review preflight. Do not tell colleagues that every rejected Python invocation erased an earlier packet; that would describe a control the implementation does not have.

Missing or corrupt evidence also needs a defined boundary. The constructor refuses a missing or changed fixed account_cards.json. Guarded operations reread that fixed fixture and check the cached evidence against the expected canonical state. A mismatch holds the operation and clears current approvals. These checks detect departure from the original teaching data, not a false assertion in an otherwise unchanged source. The lab has no arbitrary source path or URL argument. A useful real service would need its own authorised acquisition and validation process; these fixture pins are not a substitute.

Historical copies cannot be recalled by changing session state. export_internal_review_packet returns a deep copy, and a learner may later save or print that copy outside the lab. Invalidation marks stored export-history entries currentlyEligible false and clears the current export object, but does not alter the already returned copy. Retain the source and brief revisions on every copy. A correction record should explain which version was superseded and what recipients would need to be told in an actual approved workflow, without claiming that this exercise sends those notifications.

Process reset is another distinct event. A new PipitLab object or a new process starts empty at source revision one, because the original disk fixture is unchanged and no session database is loaded. The revision-two change exists only within the session that applied it. A reset does not preserve an assumed review, an earlier export or a correction history. This makes the practice portable, but it is not production persistence or a backup system. If a learner needs a historical practice record, they must understand the limits of a separately saved output.

The capstone combines the earlier decisions into one reproducible packet. The reader should see the correct target entity, as-of date, card revisions, source-origin graph, recorded timeline, narrow statements, hypotheses, unknowns, omissions and held outreach docket. Include every material challenge, including a budget or quotation that has no evidential source. Nine completed classifications are required to build the internal draft, yet all nine unknown entries remain unresolved. The achievement is faithful treatment of supplied evidence, not verification of a real buying opportunity.

Use a six-step capstone method. First obtain the emitted worksheet and identify the nine atomic assertions. Second inspect the prescribed local sources and complete entity, origin and time checks. Third choose narrow wording and assemble the complete internal draft. Fourth challenge the draft's weakest links and record a current assumed review. Fifth rehearse a stop and P11 withdrawal, rebuilding affected checks and the aggregate review. Sixth return the corrected internal packet with version history and continuing action limits. A printed source packet can support the same reasoning without any model.

Judge the result by reconstruction and control. Another reader should explain why P02 belongs to a different entity, why P03/P04 provide one announcement origin, why P05 qualifies the site story and why P07 supplies no verified budget or buyer quotation. They should see P08/P09 absent and P10's objection preserved. They should also identify the withdrawn invitation without overstating other procurement. Complete source review, actual supplier permissions, qualified applicability review, accessible delivery and practitioner acceptance remain separate work. Neither the capstone nor a knowledge score confers them.

Scroll sideways to see every column.

Produce, challenge and recover a dated account brief · Table 14
Prompt componentCapstone instruction
RoleInternal account-evidence organiser and correction recorder.
ConstraintsEleven supplied cards, nine atomic assertions, exact IDs, fixed 1 October as-of date; no live sources or external action.
ProcessInspect; bind; trace; record time; select finite wording; build; review; stop; change P11; resume; recheck B08; rebuild/review.
VerificationMatch card and brief revisions, source sets, chronological events and current review hash. Keep historic copies distinct.
Output schemaInternal packet; unknowns; omissions; sourceOriginMap; eventTimelines; held docket; source/brief revisions; correction history.
Failure modesHold missing evidence, stale wording/review, forged token and excluded action. Distinguish handled rejection from pre-body TypeError.

Scroll sideways to see every column.

Produce, challenge and recover a dated account brief · Table 15
Worked recovery stageResult and limit
Average: Change P11 and reuse the old reviewed packet because most facts are unchanged.B08 and the aggregate approval are stale. Unaffected classifications do not keep the old packet eligible.
Elite: Stop and apply the permitted P11 change.stop_workflow('source_change'); change_source_revision('P11',2). Eight unaffected classifications remain; current brief/review/export readiness is cleared.
Resume and recheckresume_workflow(1) is held while sourceRevision=2. resume_workflow(2) restores no review. Open P11 at2 and complete B08 entity/origin/time checks; select W08_WITHDRAWN.
New aggregate reviewBuild a new INTERNAL_DRAFT; use its current briefRevision in record_review. Return a fresh INTERNAL_REVIEW_PACKET with all authority flags false.
Previous copyStored export history becomes ineligible. An already returned or manually saved copy remains a historical snapshot outside the session.

Withdraw, recheck and review the corrected brief

A source change removes current aggregate approval while preserving unaffected classifications and history.

Earlier internal packetStop workflowP11 revision 2Historical copiesResume and recheck B08Rebuild and new review
Steps
  • Earlier internal packet: Original P11 notice, current assumed review.
  • Stop workflow: Clear approvals; source change allowed while paused.
  • P11 revision 2: 28 September withdrawal; B08 and aggregate approval invalidated.
  • Historical copies: Stored eligibility false; returned copies cannot be recalled.
  • Resume and recheck B08: Current revision2, exact checks and W08_WITHDRAWN.
  • Rebuild and new review: Fresh current brief/review; internal packet only.
Relationships and conditions
  1. Earlier internal packet to Stop workflow: Exercise human control
  2. Stop workflow to P11 revision 2: Apply permitted withdrawal
  3. P11 revision 2 to Historical copies: Retain old version as history
  4. P11 revision 2 to Resume and recheck B08: Resume without restored review
  5. Resume and recheck B08 to Rebuild and new review: Review complete corrected packet

Text equivalent: Stop the earlier workflow and apply P11 revision two. B08 and the aggregate approval are invalidated; unaffected classifications remain. Stored old exports are marked ineligible, while returned copies remain historical. Resume at current revision two, inspect and recheck B08 with W08_WITHDRAWN, then build and review a new complete internal packet. Outreach remains excluded.

Try it yourself · Activity 12

120 minutes

Complete and recover the Pipit account evidence packet

Complete the blank nine-assertion worksheet from learner_materials() using the four portable files or printed cards. Build a labelled internal-review packet, then rehearse the P11 withdrawal with an explicit stop and current-revision recovery. Record both evidence reasoning and the limits of what the lab controls.

  1. Success criteria: account for all nine atomic assertions with target entity, exact source/event sets, revisions, narrow wording and explicit unknowns.
  2. Retain P03/P04's shared announcement origin and P05 cancellation; reject P07's invented budget and quotation; keep P08/P09 bodies absent.
  3. Preserve P10's objection, UNKNOWN subscriber class, DO_NOT_CONTACT and NOT_ASSESSED without external action.
  4. Build all nine classifications before recording a current assumed review; explain that the token is not actual identity or professional authority.
  5. Stop, change P11 to2, resume at current revision2, recheck B08 with W08_WITHDRAWN and build/review the new aggregate packet.
  6. Distinguish handled rejection, pre-body TypeError, old returned copies and new-process reset; describe paper predictions separately from any observed run.

Which evidence change invalidated the combined packet, which classifications survived, and how would you communicate a correction through an actually authorised organisation?

Worked answer

Completed baseline paper worksheet. Every row uses asOf=2026-10-01. Sources are exact inspected sets, event IDs are the prescribed chronological lists, and source bindings below identify each card's initial revision. P10 is inspected metadata, not a readable body. For each row bind_entity uses target WR-C01, including B02: P02 itself belongs to WE-C02 and demonstrates the mismatch. propose_statement returns status BOUNDED_STATEMENT with the row's distinct disposition. B01 | target=WR-C01 | sources=[P01] | events=[P01] | source bindings=P01@1 | wording=W01 | disposition=OPERATING_SITE_UNKNOWN | bounded meaning: P01 records active status and registered Office Token RO-1 on 1 September 2026; it does not establish an operating site. | unknown: Operating site (resolved=false). B02 | target=WR-C01 | sources=[P02] | events=[P02] | source bindings=P02@1 | wording=W02 | disposition=WRONG_ENTITY | bounded meaning: The P02 demonstration concerns WE-C02, not target WR-C01. The target's attendance is unknown. | unknown: Target attendance (resolved=false). B03 | target=WR-C01 | sources=[P03, P05] | events=[P03, P05] | source bindings=P03@1, P05@1 | wording=W03 | disposition=SUPERSEDED_PLAN | bounded meaning: WR-C01 proposed a second site on 3 May and cancelled that proposal on 20 May 2026. Present expansion is not established. | unknown: Other current expansion plans (resolved=false). B04 | target=WR-C01 | sources=[P03, P04, P05] | events=[P03, P04, P05] | source bindings=P03@1, P04@1, P05@1 | wording=W04 | disposition=DEPENDENT_ORIGIN | bounded meaning: P03 and P04 share the expansion-announcement origin: two cards, one origin. P05 separately records cancellation. | unknown: Independent confirmation (resolved=false). B05 | target=WR-C01 | sources=[P07] | events=[] | source bindings=P07@1 | wording=W05 | disposition=UNSUPPORTED | bounded meaning: No admitted evidence supplies an approved container budget. The GBP150,000 figure appears only in the faulty teaching draft. | unknown: Approved purchase budget (resolved=false). B06 | target=WR-C01 | sources=[P06] | events=[P06] | source bindings=P06@1 | wording=W06 | disposition=HYPOTHESIS_ONLY | bounded meaning: The 8 June vacancy is a recorded advertisement. A process-review question may be a hypothesis; current vacancy status and purchase intent remain unknown. | unknown: Current vacancy and purchase intent (resolved=false). B07 | target=WR-C01 | sources=[P07] | events=[] | source bindings=P07@1 | wording=W07 | disposition=UNSUPPORTED_QUOTATION | bounded meaning: No admitted source supplies the buyer quotation. The authored challenge cannot verify its own quotation. | unknown: Buyer preference and decision authority (resolved=false). B08 | target=WR-C01 | sources=[P11] | events=[P11] | source bindings=P11@1 | wording=W08 | disposition=HISTORICAL_NOTICE | bounded meaning: P11 records a 10 June invitation with a stated 24 June closing date. Procurement status on 1 October 2026 is unknown. | unknown: Current procurement status (resolved=false). B09 | target=WR-C01 | sources=[P10] | events=[P10] | source bindings=P10@1 | wording=W09 | disposition=HELD_ACTION | bounded meaning: The fixture records a prior marketing objection for R-T01 and unknown subscriber class. Its action docket is DO_NOT_CONTACT; real permission is NOT_ASSESSED. | unknown: Applicable subscriber classification and actual permission (resolved=false).

Completed internal packet outline: status=INTERNAL_REVIEW_PACKET only after the complete INTERNAL_DRAFT and a current ASSUMED_INTERNAL_REVIEW. Header: entityId=WR-C01, asOf=2026-10-01, sourceRevision=1, actual current briefRevision and bound brief hash. Evidence section: the nine worksheet entries above, sourceBindings, sourceOriginMap and eventTimelines. The origin map retains P03/P04's one expansion-announcement origin and P05's separate cancellation-update; it does not count two independent expansion confirmations. Unknown register: exactly the nine named unresolved gaps above. Omissions: P08 irrelevant personal body absent; P09 rights-restricted body absent. Held docket: recipient token R-T01, priorMarketingObjection=true, subscriberClass=UNKNOWN, fixtureAction=DO_NOT_CONTACT, realPermission=NOT_ASSESSED. Review section: pipit-assumed-reviewer, the current briefRevision and evidence_checked or manual_review. All actual account/identity/reviewer/basis/consent/rights/outreach/model/external-action flags remain false; export returns a copy with filesWritten=0. This outline is a worked paper key, not a report of an executed run or real approval.

Completed change rehearsal: stop_workflow('source_change'); change_source_revision('P11',2); resume_workflow(2), not1. B08 checks and the aggregate brief/review/current export approval are invalidated, while eight unaffected classifications remain. open_company_card('P11',2); bind_entity('B08','WR-C01'); trace_origin('B08',['P11']); record_timeline('B08',['P11'],'2026-10-01'); propose_statement('B08','W08_WITHDRAWN'). The revised row binds P11@2, status BOUNDED_STATEMENT, disposition WITHDRAWN_NOTICE, retaining the 10 June invitation and 24 June closing date with the 28 September withdrawal; other current procurement remains unknown. Build a new aggregate brief, use its actual new briefRevision for a fresh assumed review, then return the revised packet. Resume does not restore review. If an obsolete wording causes a handled rejection, repeat affected entity/origin/time checks before retrying. Stored old export eligibility becomes false; already returned copies remain historical outside recall. A handled method-body rejection clears current approvals; pre-body Python TypeError does not. A new object/process resets to source revision1, empty classifications and no review. No model, actual account, rights or outreach approval is established.

Scroll sideways to see every column.

Produce, challenge and recover a dated account brief · Table 16
Chapter review questionAnswer after your attempt
How do card, source and brief revisions differ?Card revision binds an individual source, sourceRevision marks the fixture dataset state, and briefRevision increments when a complete draft is built.
What does the P11 change invalidate?B08 checks/classification and every current aggregate brief/review/export approval. The other eight unaffected classifications remain.
Why does resume_workflow(2) not make export ready?It restores no review. B08 must be rechecked and the complete corrected brief rebuilt and freshly reviewed.
What happens after a pre-body TypeError?The method body never runs, so prior state remains; a later valid export still performs integrity/review preflight.
Can invalidation recall an old returned packet?No. Internal history eligibility is marked false, but an independently returned/saved copy remains a historical snapshot.
What does a new process preserve?No session review or change state. It starts at original disk-fixture revision1 with empty classifications; it is not persistent recovery.

Apply what you learned · Practice

After P11 withdrawal and a correct resume, eight unaffected classifications remain. What is needed before another packet can be returned?

Explanation and answer

Recheck B08, build the complete corrected brief and record a fresh current assumed review. Resumption restores no review. The affected assertion and aggregate packet need current checks and a new assumed review; a new process would reset to revision1.

Practice only. This does not count towards your exam or certificate.

Six-step Pipit withdrawal diagram: stop, revise P11, invalidate, resume at2, recheck B08 and obtain a fresh review. Historical copies remain outside recall.
Original illustration of the fictional P11 revision2 transition. Resume does not restore review; the returned packet remains internal only.

Text equivalent: Initial B08 uses W08 with disposition HISTORICAL_NOTICE at sourceRevision1. Stop the workflow and apply the P11 revision2 fixture transition: its fictional 28 September withdrawal retains the 10 June invitation and stated 24 June closing date. This invalidates B08 checks and current aggregate brief, review and export approval; eight unaffected classifications remain. Resume only at exact current sourceRevision2; review is not restored. Re-open P11 at2, bind B08 to WR-C01, trace P11, record its timeline at the fixed 1 October as-of and use W08_WITHDRAWN with disposition WITHDRAWN_NOTICE. Build the new complete brief and obtain a fresh current assumed review before returning a new internal packet. Stored old export history becomes currentlyEligible=false, but copies already returned or saved cannot be recalled. Outreach remains held, and the fixture reviewer token is not actual identity or professional authority.

Scroll sideways to see every column.

Produce, challenge and recover a dated account brief · Table 17
Supplementary questionAnswer after your attempt
Why does the lab omit the personal and restricted report bodies?P08 is irrelevant personal detail and P09 has unresolved access/rights. Their distinct reasons are recorded without loading either body.
Does a strong prompt control a real provider's retention or training?No. The prompt proposes a bounded task; actual supplier processing, further use and contractual controls need their own review. No provider is used here.
Does finite wording acceptance mean the lab can check a new prospect report?No. It checks a fixed fixture and wording IDs with explicit dependencies, not arbitrary prose or real-account truth.
Are worksheet and template extra downloadable files?No. learner_materials() and the worksheet/template commands emit them. The portable source set is exactly README.md, account_cards.json, pipit_lab.py and test_pipit_lab.py.
Does a reviewed company brief authorise a marketing message?No. The fixture's internal review is an assumption. P10's objection and unresolved subscriber/real permission remain in a separate held action docket.
Can the B2B guidance's six-bases sentence be taught as current?No. That page is under review and its old sentence differs from updated March 2026 ico-legitimate-interests terminology. The course selects no real basis.
Does withdrawal erase a packet already saved elsewhere?No. Current eligibility and the session's approvals are invalidated, but previously returned or manually saved copies remain historical outside the lab.
Does a fresh process resume the corrected revision2 session?No. The original fixture remains revision1 on disk and no session is persisted. A new process starts empty with no review or export.
  • Account question: target WR-C01, fixed1October2026 as-of and intended internal receiving decision.
  • Source register: eleven original cards; eight readable synthetic bodies and three metadata-only records; no P08/P09/P10 body reconstruction.
  • Assertion worksheet: B01-B09 with exact source/event sets, bound card revisions, accepted wording IDs, narrow meanings and nine unresolved gaps.
  • Entity boundary: P02 belongs to WE-C02; an office token is not an operating-site finding.
  • Origin boundary: P03/P04 share the expansion-announcement; P05 independently records a cancellation of that proposal.
  • Time boundary: distinguish event, publication, retrieval and current use. P04 records a3May event with4May publication.
  • Permission boundary: field purpose, copying/reuse, actual processing basis, supplier handling, notices, channel and subscriber class remain separate.
  • Held docket: prior objection, UNKNOWN subscriber, DO_NOT_CONTACT and NOT_ASSESSED; no message, scrape, model, call or CRM action.
  • Human challenge: current assumed reviewer token, exact briefRevision and ability to stop; no actual identity or authority verified.
  • Correction: P11 to2, resume at exact current revision, recheck B08, W08_WITHDRAWN, rebuild and fresh review; old returned copies and process reset limits retained.
text · 94 lines
# Pipit Account Evidence Desk

Original adult sales-research teaching lab for sales-ai-prospect-research. Author: Mickarle Sean Junior Wagstaff-Irons - Micky Irons. AI-assisted code and fixture preparation. This staged lab is not a finished course, legal clearance or a live prospecting service.

## Four portable files

Copy these four files together into one local folder: README.md, account_cards.json, pipit_lab.py and test_pipit_lab.py. Python 3.10 or later and its standard library are sufficient; no packages, network connection, model or credentials are needed.

From that folder, run:

```text
python -B pipit_lab.py worksheet
python -B pipit_lab.py template
python -B -m unittest -v test_pipit_lab
python -B pipit_lab.py demo
python -B pipit_lab.py reset-probe
```

Worksheet and template are emitted to the terminal, not created as extra learner source files. The worksheet is blank; the visible finite wording menu supports an open teaching exercise. The demo is a teacher walkthrough and exposes the completed example. None is a scored exam. Read the source cards, complete a paper worksheet first, then compare your reasoning with the walkthrough. The same tasks can be completed on printed cards without AI. Actual screen-reader, learner and printer acceptance has not been established by these commands.

The lab reads only the fixed sibling account_cards.json and rejects a changed canonical fixture. It has no user-controlled input/output filename. It writes no files: export means returning an internal packet, or printing that packet in demo mode. If you choose to save terminal output yourself, it is your historical local copy; later session changes cannot recall it.

## Case and evidence

Pipit sells fictional reusable transit containers. Target WR-C01 is Westhaven Returnable Containers Ltd. WE-C02 is the distinct Westhaven Events Ltd. These IDs are not real Companies House numbers. No real companies, people, addresses or contact details are supplied.

Eleven synthetic cards cover: P01 target registry-style record; P02 another entity's event; P03 proposed site; P04 dependent trade article; P05 cancellation; P06 archived vacancy; P07 deliberately faulty authored draft; P08 excluded personal-detail metadata; P09 restricted-report metadata; P10 prior-objection/unknown-subscriber metadata; P11 old invitation-to-quote notice. P08 and P09 bodies are absent. P10 also has no body. No redaction, external lookup or report acquisition occurs.

P07 is an original authored challenge, not an observed AI response. Reading its body does not turn it into factual evidence. The fixture contains a false budget and quotation to challenge. It never establishes current purchase intent, a real account, an actual contact permission or rights to a real source.

## Nine atomic assertions

The implementation narrows two compound proposals in the frozen preparation plan: B01 asks whether the registered office establishes an operating site, and B09 asks whether Pipit has contact permission. The contextual active-status record and the independent omissions remain visible.

| ID | Assertion to challenge | Narrow finite result |
|---|---|---|
| B01 | Operating site at the registered office | OPERATING_SITE_UNKNOWN |
| B02 | Target attends the other entity's event | WRONG_ENTITY |
| B03 | Second site is currently opening | SUPERSEDED_PLAN |
| B04 | Two independent expansion confirmations | DEPENDENT_ORIGIN |
| B05 | Approved GBP150,000 budget | UNSUPPORTED |
| B06 | Vacancy proves a purchase programme | HYPOTHESIS_ONLY |
| B07 | Buyer selected Pipit in a quotation | UNSUPPORTED_QUOTATION |
| B08 | Procurement is open today | HISTORICAL_NOTICE |
| B09 | Recipient token may be contacted | HELD_ACTION |

These are code-owned cases and wording choices. They are not a general semantic classifier. Unsupported arbitrary text is held, not read by a model and checked for truth.

B04 uses P03/P04/P05. The two announcement cards share one origin; P05 is a separate cancellation update, not a second expansion confirmation. All three source passages and their scopes matter.

## Public method signatures

Create a fresh session with PipitLab(). Metadata inspection establishes access to P10 metadata, while body inspection uses positive exact integer revisions. Eight cards have readable bodies: P01-P07 and P11.

```python
from pipit_lab import PipitLab
lab = PipitLab()
lab.list_source_cards()
lab.open_company_card("P01", 1)
lab.bind_entity("B01", "WR-C01")
lab.trace_origin("B01", ["P01"])
lab.record_timeline("B01", ["P01"], "2026-10-01")
lab.propose_statement("B01", "W01")
```

Repeat entity, origin, chronology and finite wording checks for each assertion. The exact source/event/wording map is inspectable in RULES in pipit_lab.py and in the maintainer LAB-CONTRACT.json. Full brief construction requires all nine classifications. build_internal_brief() returns INTERNAL_DRAFT, with a monotonically increasing briefRevision.

record_review("pipit-assumed-reviewer", brief_revision, "evidence_checked") or reason "manual_review" records only an assumed fixture review. export_internal_review_packet() requires that exact current brief and review. It returns INTERNAL_REVIEW_PACKET with an unknown register, omitted-body reasons, source-origin map, event timelines and an unresolved outreach-review docket.

Other methods are learner_materials(), state_snapshot(), change_source_revision(card_id, revision), stop_workflow(reason), resume_workflow(expected_revision) and request_external_action(action_id). STOP reasons are learner_pause, error_found, source_change and review_required. request_external_action always holds; there are no send, scrape, model or CRM operations.

All account, identity, actual-reviewer, consent, source-rights, lawful-basis, model, external-action and outreach-authority flags remain false. The docket remains DO_NOT_CONTACT for the fictional prior objection, subscriberClass UNKNOWN and realPermission NOT_ASSESSED. Even a fully completed packet retains nine unknowns. It is useful internal teaching evidence, not permission for action.

## Revision, stop and failed-call behaviour

change_source_revision("P11", 2) performs the only allowed fixture source transition. It adds a fictional 28 September withdrawal, retaining the original 10 June notice and stated 24 June deadline. The as-of date stays 1 October. It clears B08 bindings/classification and every current brief, review and export approval; unaffected classifications remain.

Reopen P11 at revision 2, rebind/retrace/retime B08 and use W08_WITHDRAWN. Build a new brief and record a new assumed review before export. W08 cannot be reused after the withdrawal. Previous P11 source history is retained as history.

stop_workflow("source_change") clears approvals. A fixture revision change is allowed while stopped; normal work is held. resume_workflow(2) only succeeds if currently stopped at dataset revision 2. It restores no review or export. Rebuild/review are still required. No stale revision, boolean or numeric string is accepted as an integer revision.

A handled invalid argument or rejected proposal entering a method body clears current approvals. Python signature errors are different: an unexpected keyword or missing argument raises TypeError before the body, so it does not run invalidation and the earlier state remains. Do not describe every failed Python call as clearing an earlier packet. A later valid export still performs the integrity and review preflight.

Already returned packets are independent copies. Invalidation marks internal export-history entries currentlyEligible=false; it cannot erase a copy previously printed or saved elsewhere. state_snapshot() is an observer, not an approval check. Its fixedFixtureReads count includes preflight rereads; bodyReads counts the distinct logical card-open operations, not all filesystem reads.

Every new object and new process starts empty at source revision 1. No session or review is persisted. The original fixed fixture remains unchanged on disk after revision2, which exists only in that session's memory.

## Scope of the runtime checks

The included tests exercise finite entity/origin/time boundaries, unsupported words, omissions, objections, missing/corrupt evidence, revisions, review tokens, stop/resume, invalidation, TypeError distinction, copied outputs and actual child-process reset. Tests may create and remove a temporary relocated fixture under this same exercise folder; they never require real account data or network access.

Tests are author checks, not independent practitioner approval. Their observed count and input hashes are in the maintainer IMPLEMENTATION-REVIEW.json alongside LAB-CONTRACT.json; those two files are not extra learner appendices. The four portable files are enough to rerun the suite.

This is inspectable teaching Python, not a security sandbox against someone editing its source, constants or private attributes. Canonical hashes detect a changed fixture; they do not prove an assertion is true or a legal permission exists. Real source use, privacy/direct marketing, supplier controls, qualified review, accessible delivery and full course publication remain unresolved.

json · 137 lines
{
  "schemaVersion": 1,
  "case": "Pipit Account Evidence Desk",
  "fictional": true,
  "asOf": "2026-10-01",
  "entities": {
    "WR-C01": "Westhaven Returnable Containers Ltd",
    "WE-C02": "Westhaven Events Ltd"
  },
  "cards": [
    {
      "id": "P01",
      "kind": "company",
      "entityId": "WR-C01",
      "originId": "register-card",
      "revision": 1,
      "publicationDate": "2026-09-01",
      "eventDate": "2026-09-01",
      "retrievedAt": "2026-10-01",
      "body": "Fictional registry-style record dated 1 September 2026: WR-C01 is recorded as active. Its registered office is Office Token RO-1. This record does not identify an operating site. These tokens are not real registry or address data."
    },
    {
      "id": "P02",
      "kind": "company",
      "entityId": "WE-C02",
      "originId": "events-register-card",
      "revision": 1,
      "publicationDate": "2026-09-02",
      "eventDate": "2026-09-02",
      "retrievedAt": "2026-10-01",
      "body": "Fictional WE-C02 record: Westhaven Events Ltd lists a trade demonstration on 15 October 2026. WE-C02 is separate from WR-C01. No connection between their projects is supplied."
    },
    {
      "id": "P03",
      "kind": "company",
      "entityId": "WR-C01",
      "originId": "expansion-announcement",
      "revision": 1,
      "publicationDate": "2026-05-03",
      "eventDate": "2026-05-03",
      "retrievedAt": "2026-10-01",
      "body": "Fictional company announcement, 3 May 2026: WR-C01 proposes a second container-handling site, subject to review. No opened site, approved purchase or budget is stated."
    },
    {
      "id": "P04",
      "kind": "company",
      "entityId": "WR-C01",
      "originId": "expansion-announcement",
      "revision": 1,
      "publicationDate": "2026-05-04",
      "eventDate": "2026-05-03",
      "retrievedAt": "2026-10-01",
      "body": "Fictional trade article, 4 May 2026: this article repeats the 3 May WR-C01 announcement and credits that announcement as its source. It supplies no independent confirmation."
    },
    {
      "id": "P05",
      "kind": "company",
      "entityId": "WR-C01",
      "originId": "cancellation-update",
      "revision": 1,
      "publicationDate": "2026-05-20",
      "eventDate": "2026-05-20",
      "retrievedAt": "2026-10-01",
      "body": "Fictional company update, 20 May 2026: WR-C01 cancelled the second-site proposal announced on 3 May. This update does not describe every later business plan."
    },
    {
      "id": "P06",
      "kind": "company",
      "entityId": "WR-C01",
      "originId": "vacancy-card",
      "revision": 1,
      "publicationDate": "2026-06-08",
      "eventDate": "2026-06-08",
      "retrievedAt": "2026-10-01",
      "body": "Fictional archived vacancy, 8 June 2026: WR-C01 advertised a coordinator role. Current vacancy status, container purchase intent and a purchase budget are not supplied."
    },
    {
      "id": "P07",
      "kind": "authored-challenge",
      "entityId": "MIXED",
      "originId": null,
      "revision": 1,
      "publicationDate": null,
      "eventDate": null,
      "retrievedAt": "2026-10-01",
      "body": "Deliberately faulty authored teaching draft, not a model response: WR-C01 operates at its registered office, attends the WE-C02 event and is opening the second site. Two independent reports confirm expansion. Its approved container budget is GBP150,000. The vacancy proves a purchase programme. A buyer said: 'We have selected Pipit.' Procurement is open today. Public profile material makes outreach permitted. These are assertions to challenge, not verified account facts."
    },
    {
      "id": "P08",
      "kind": "excluded-metadata",
      "entityId": "PERSON-TOKEN",
      "originId": null,
      "revision": 1,
      "publicationDate": null,
      "eventDate": null,
      "retrievedAt": "2026-10-01",
      "exclusionReason": "Irrelevant personal-interest detail; body is absent, not redacted at runtime."
    },
    {
      "id": "P09",
      "kind": "restricted-metadata",
      "entityId": "UNKNOWN",
      "originId": null,
      "revision": 1,
      "publicationDate": null,
      "eventDate": null,
      "retrievedAt": "2026-10-01",
      "title": "Fictional account market report",
      "exclusionReason": "Rights and access unresolved; body is absent and title is not evidence of contents."
    },
    {
      "id": "P10",
      "kind": "preference-metadata",
      "entityId": "WR-C01",
      "originId": "preference-card",
      "revision": 1,
      "publicationDate": "2026-10-01",
      "eventDate": "2026-10-01",
      "retrievedAt": "2026-10-01",
      "recipientToken": "R-T01",
      "priorMarketingObjection": true,
      "subscriberClass": "UNKNOWN",
      "realPermission": "NOT_ASSESSED"
    },
    {
      "id": "P11",
      "kind": "company",
      "entityId": "WR-C01",
      "originId": "invitation-card",
      "revision": 1,
      "publicationDate": "2026-06-10",
      "eventDate": "2026-06-10",
      "retrievedAt": "2026-10-01",
      "body": "Fictional invitation-to-quote notice dated 10 June 2026: WR-C01 stated a closing date of 24 June 2026. Current procurement status, approved budget and selected supplier are not supplied."
    }
  ]
}

python · 468 lines
"""Pipit: finite synthetic account evidence, no external action or model.

This is inspectable teaching code, not a sandbox against someone modifying Python.
Only the fixed sibling account_cards.json is read. Public methods accept fixture IDs.
"""
from __future__ import annotations

import argparse
from copy import deepcopy
from datetime import date
import hashlib
import json
from pathlib import Path
import sys

BASE_HASH = "aed170fd587e9d5d3af69b1feb04beffc6db2fe8071aedeeef3b08b5b54ba76d"
AS_OF = "2026-10-01"
TARGET = "WR-C01"
REVIEWER = "pipit-assumed-reviewer"
REASONS = ("evidence_checked", "manual_review")
STOP_REASONS = ("learner_pause", "error_found", "source_change", "review_required")
FLAGS = {
    "actualAccountVerified": False, "actualIdentityVerified": False,
    "actualReviewerVerified": False, "legalBasisSelected": False,
    "actualConsentVerified": False, "outreachAuthorised": False,
    "realSourceRightsVerified": False, "modelUsed": False,
    "externalActionPerformed": False,
}
RULES = json.loads(r'''[{"id":"B01","claim":"WR-C01 operates at the registered-office address.","sources":["P01"],"events":["P01"],"wording":"W01","disposition":"OPERATING_SITE_UNKNOWN","text":"P01 records active status and registered Office Token RO-1 on 1 September 2026; it does not establish an operating site.","unknown":"Operating site","bad":"OPERATES_AT_OFFICE"},{"id":"B02","claim":"WR-C01 is attending the P02 trade demonstration.","sources":["P02"],"events":["P02"],"wording":"W02","disposition":"WRONG_ENTITY","text":"The P02 demonstration concerns WE-C02, not target WR-C01. The target's attendance is unknown.","unknown":"Target attendance","bad":"TARGET_ATTENDS_EVENT"},{"id":"B03","claim":"WR-C01 is currently opening the second site.","sources":["P03","P05"],"events":["P03","P05"],"wording":"W03","disposition":"SUPERSEDED_PLAN","text":"WR-C01 proposed a second site on 3 May and cancelled that proposal on 20 May 2026. Present expansion is not established.","unknown":"Other current expansion plans","bad":"EXPANSION_IS_CURRENT"},{"id":"B04","claim":"Two independent publications confirm the expansion.","sources":["P03","P04","P05"],"events":["P03","P04","P05"],"wording":"W04","disposition":"DEPENDENT_ORIGIN","text":"P03 and P04 share the expansion-announcement origin: two cards, one origin. P05 separately records cancellation.","unknown":"Independent confirmation","bad":"TWO_INDEPENDENT_REPORTS"},{"id":"B05","claim":"WR-C01 has an approved GBP150,000 container budget.","sources":["P07"],"events":[],"wording":"W05","disposition":"UNSUPPORTED","text":"No admitted evidence supplies an approved container budget. The GBP150,000 figure appears only in the faulty teaching draft.","unknown":"Approved purchase budget","bad":"BUDGET_150000"},{"id":"B06","claim":"The vacancy proves a container-purchase programme.","sources":["P06"],"events":["P06"],"wording":"W06","disposition":"HYPOTHESIS_ONLY","text":"The 8 June vacancy is a recorded advertisement. A process-review question may be a hypothesis; current vacancy status and purchase intent remain unknown.","unknown":"Current vacancy and purchase intent","bad":"VACANCY_PROVES_PURCHASE"},{"id":"B07","claim":"A buyer said they selected Pipit.","sources":["P07"],"events":[],"wording":"W07","disposition":"UNSUPPORTED_QUOTATION","text":"No admitted source supplies the buyer quotation. The authored challenge cannot verify its own quotation.","unknown":"Buyer preference and decision authority","bad":"BUYER_QUOTE_VERIFIED"},{"id":"B08","claim":"The invitation proves procurement is open today.","sources":["P11"],"events":["P11"],"wording":"W08","disposition":"HISTORICAL_NOTICE","text":"P11 records a 10 June invitation with a stated 24 June closing date. Procurement status on 1 October 2026 is unknown.","unknown":"Current procurement status","bad":"PROCUREMENT_OPEN_TODAY"},{"id":"B09","claim":"Pipit has permission to contact recipient token R-T01.","sources":["P10"],"events":["P10"],"wording":"W09","disposition":"HELD_ACTION","text":"The fixture records a prior marketing objection for R-T01 and unknown subscriber class. Its action docket is DO_NOT_CONTACT; real permission is NOT_ASSESSED.","unknown":"Applicable subscriber classification and actual permission","bad":"OUTREACH_READY"}]''')
RULE_MAP = {rule["id"]: rule for rule in RULES}
READABLE = ("P01", "P02", "P03", "P04", "P05", "P06", "P07", "P11")
WITHDRAWAL = {
    "publicationDate": "2026-09-28", "eventDate": "2026-09-28", "revision": 2,
    "body": "Fictional update dated 28 September 2026: the WR-C01 invitation originally dated 10 June, with a stated 24 June closing date, was withdrawn. This records withdrawal of that invitation, not every later procurement plan.",
}
WITHDRAWN_TEXT = "P11 revision 2 retains the original 10 June invitation and stated 24 June closing date, and records its withdrawal on 28 September 2026. Other current procurement activity remains unknown."


def canonical_hash(value):
    raw = json.dumps(value, sort_keys=True, separators=(",", ":"), ensure_ascii=False)
    return hashlib.sha256(raw.encode("utf-8")).hexdigest()


def exact_int(value):
    return type(value) is int and value > 0


def id_list(value):
    return (type(value) is list and all(type(x) is str for x in value)
            and len(set(value)) == len(value))


class FixtureError(ValueError):
    """Missing, changed or invalid fixed local fixture; no session is created."""


class PipitLab:
    def __init__(self):
        self._path = Path(__file__).resolve().with_name("account_cards.json")
        self._fixture_reads = 0
        try:
            baseline = self._read_fixed()
        except (OSError, ValueError) as exc:
            raise FixtureError("Fixed account_cards.json is missing or corrupt") from exc
        if canonical_hash(baseline) != BASE_HASH:
            raise FixtureError("Fixed account_cards.json does not match the original fixture")
        self._baseline = deepcopy(baseline)
        self._data = deepcopy(baseline)
        self._expected_hash = BASE_HASH
        self._cards = {c["id"]: c for c in self._data["cards"]}
        self._source_revision = 1
        self._brief_counter = 0
        self._stopped = False
        self._opened = {}
        self._metadata_seen = set()
        self._entities = {}
        self._origins = {}
        self._timelines = {}
        self._statements = {}
        self._brief = None
        self._review = None
        self._current_export = None
        self._export_history = []
        self._source_history = []
        self._audit = []
        self._body_reads = []

    def _read_fixed(self):
        # No user-controlled pathname or URL. Do not follow a fixture symlink.
        if self._path.is_symlink() or self._path.resolve() != self._path:
            raise FixtureError("Fixture location must be the fixed local sibling")
        self._fixture_reads += 1
        return json.loads(self._path.read_text(encoding="utf-8"))

    def _invalidate(self, event):
        self._brief = None
        self._review = None
        self._current_export = None
        for old in self._export_history:
            old["currentlyEligible"] = False
        self._audit.append({"event": event, "sourceRevision": self._source_revision})

    def _reject(self, reason, assertion_id=None, card_id=None):
        self._invalidate("handled_rejection:" + reason)
        if assertion_id in RULE_MAP:
            for store in (self._entities, self._origins, self._timelines, self._statements):
                store.pop(assertion_id, None)
        if card_id in self._cards:
            self._opened.pop(card_id, None)
            self._metadata_seen.discard(card_id)
            for rule in RULES:
                if card_id in rule["sources"]:
                    self._origins.pop(rule["id"], None)
                    self._timelines.pop(rule["id"], None)
                    self._statements.pop(rule["id"], None)
        return {"status": "HOLD", "reason": reason, "sourceRevision": self._source_revision,
                **deepcopy(FLAGS)}

    def _guard(self, allow_stopped=False):
        try:
            disk = self._read_fixed()
            if canonical_hash(disk) != BASE_HASH:
                return self._reject("CORRUPT_OR_MISSING_FIXTURE")
        except (OSError, ValueError):
            return self._reject("CORRUPT_OR_MISSING_FIXTURE")
        if canonical_hash(self._data) != self._expected_hash:
            return self._reject("CORRUPT_EVIDENCE")
        if self._stopped and not allow_stopped:
            return self._reject("STOPPED")
        return None

    def _rule(self, assertion_id):
        if type(assertion_id) is not str:
            return None
        return RULE_MAP.get(assertion_id)

    def _source_ready(self, source_id):
        if source_id in READABLE:
            return self._opened.get(source_id) == self._cards[source_id]["revision"]
        return source_id == "P10" and source_id in self._metadata_seen

    def list_source_cards(self):
        held = self._guard()
        if held:
            return held
        first = not self._metadata_seen
        self._metadata_seen.update(("P08", "P09", "P10"))
        if first:
            self._invalidate("metadata_inspected")
        rows = [{k: deepcopy(v) for k, v in c.items() if k != "body"}
                for c in self._data["cards"]]
        for row in rows:
            row["bodyAvailable"] = row["id"] in READABLE
        return {"status": "METADATA", "cards": rows, **deepcopy(FLAGS)}

    def open_company_card(self, card_id, expected_revision):
        held = self._guard()
        if held:
            return held
        if type(card_id) is not str or card_id not in self._cards:
            return self._reject("UNKNOWN_CARD")
        if card_id not in READABLE:
            return self._reject("BODY_EXCLUDED_OR_ABSENT", card_id=card_id)
        card = self._cards[card_id]
        if not exact_int(expected_revision) or expected_revision != card["revision"]:
            return self._reject("SOURCE_REVISION_MISMATCH", card_id=card_id)
        self._invalidate("body_inspected:" + card_id)
        self._opened[card_id] = card["revision"]
        self._body_reads.append({"cardId": card_id, "revision": card["revision"]})
        return {"status": "LOCAL_CARD", "card": deepcopy(card),
                "bodyHash": canonical_hash(card["body"]),
                "acceptedAsFactualEvidence": card["kind"] == "company",
                **deepcopy(FLAGS)}

    def bind_entity(self, assertion_id, entity_id):
        held = self._guard()
        if held:
            return held
        if not self._rule(assertion_id):
            return self._reject("UNKNOWN_ASSERTION")
        if type(entity_id) is not str or entity_id != TARGET:
            return self._reject("TARGET_ENTITY_MISMATCH", assertion_id)
        self._invalidate("entity_bound:" + assertion_id)
        self._entities[assertion_id] = TARGET
        self._origins.pop(assertion_id, None)
        self._timelines.pop(assertion_id, None)
        self._statements.pop(assertion_id, None)
        return {"status": "FIXTURE_ENTITY_BOUND", "entityId": TARGET, **deepcopy(FLAGS)}

    def trace_origin(self, assertion_id, source_ids):
        held = self._guard()
        if held:
            return held
        rule = self._rule(assertion_id)
        if not rule:
            return self._reject("UNKNOWN_ASSERTION")
        if self._entities.get(assertion_id) != TARGET:
            return self._reject("ENTITY_NOT_BOUND", assertion_id)
        if not id_list(source_ids) or sorted(source_ids) != sorted(rule["sources"]):
            return self._reject("SOURCE_SET_MISMATCH", assertion_id)
        if not all(self._source_ready(s) for s in source_ids):
            return self._reject("SOURCE_NOT_INSPECTED", assertion_id)
        self._invalidate("origin_traced:" + assertion_id)
        rows = [{"cardId": s, "entityId": self._cards[s]["entityId"],
                 "originId": self._cards[s]["originId"],
                 "revision": self._cards[s]["revision"],
                 "kind": self._cards[s]["kind"],
                 "targetEntityMatches": self._cards[s]["entityId"] == TARGET}
                for s in sorted(source_ids)]
        evidence_origins = sorted({r["originId"] for r in rows
                                   if r["kind"] == "company" and r["originId"]})
        trace = {"sources": rows, "distinctCompanyOrigins": evidence_origins,
                 "expansionConfirmationOrigins": ["expansion-announcement"]
                    if assertion_id == "B04" else [],
                 "sourceRevision": self._source_revision}
        self._origins[assertion_id] = trace
        self._timelines.pop(assertion_id, None)
        self._statements.pop(assertion_id, None)
        return {"status": "ORIGIN_TRACED", **deepcopy(trace), **deepcopy(FLAGS)}

    def record_timeline(self, assertion_id, event_ids, as_of):
        held = self._guard()
        if held:
            return held
        rule = self._rule(assertion_id)
        if not rule:
            return self._reject("UNKNOWN_ASSERTION")
        if assertion_id not in self._origins:
            return self._reject("ORIGIN_NOT_TRACED", assertion_id)
        if type(as_of) is not str or as_of != AS_OF:
            return self._reject("AS_OF_MISMATCH", assertion_id)
        if not id_list(event_ids) or set(event_ids) != set(rule["events"]):
            return self._reject("EVENT_SET_MISMATCH", assertion_id)
        ordered = sorted(event_ids, key=lambda s: (self._cards[s]["eventDate"], s))
        if event_ids != ordered:
            return self._reject("NON_CHRONOLOGICAL_EVENTS", assertion_id)
        events = [{"cardId": s, "eventDate": self._cards[s]["eventDate"],
                   "publicationDate": self._cards[s]["publicationDate"],
                   "retrievedAt": self._cards[s]["retrievedAt"],
                   "revision": self._cards[s]["revision"]} for s in event_ids]
        if any(date.fromisoformat(e["eventDate"]) > date.fromisoformat(AS_OF) for e in events):
            return self._reject("FUTURE_EVENT", assertion_id)
        self._invalidate("timeline_recorded:" + assertion_id)
        timeline = {"asOf": AS_OF, "events": events, "sourceRevision": self._source_revision}
        self._timelines[assertion_id] = timeline
        self._statements.pop(assertion_id, None)
        return {"status": "TIMELINE_RECORDED", **deepcopy(timeline), **deepcopy(FLAGS)}

    def propose_statement(self, assertion_id, wording_id):
        held = self._guard()
        if held:
            return held
        rule = self._rule(assertion_id)
        if not rule:
            return self._reject("UNKNOWN_ASSERTION")
        accepted = rule["wording"]
        if assertion_id == "B08" and self._source_revision == 2:
            accepted = "W08_WITHDRAWN"
        if type(wording_id) is not str or wording_id != accepted:
            return self._reject("WORDING_NOT_SUPPORTED", assertion_id)
        if (self._entities.get(assertion_id) != TARGET or assertion_id not in self._origins
                or assertion_id not in self._timelines
                or not all(self._source_ready(s) for s in rule["sources"])):
            return self._reject("CHECKS_INCOMPLETE", assertion_id)
        self._invalidate("statement_recorded:" + assertion_id)
        disposition = rule["disposition"]
        text = rule["text"]
        if assertion_id == "B08" and self._source_revision == 2:
            disposition, text = "WITHDRAWN_NOTICE", WITHDRAWN_TEXT
        statement = {"assertionId": assertion_id, "challengedClaim": rule["claim"],
                     "wordingId": wording_id, "disposition": disposition, "text": text,
                     "entityId": TARGET, "asOf": AS_OF,
                     "sourceBindings": {s: self._cards[s]["revision"] for s in rule["sources"]},
                     "unknown": rule["unknown"]}
        self._statements[assertion_id] = statement
        return {"status": "BOUNDED_STATEMENT", **deepcopy(statement), **deepcopy(FLAGS)}

    def build_internal_brief(self):
        held = self._guard()
        if held:
            return held
        if set(self._statements) != set(RULE_MAP):
            return self._reject("ASSERTIONS_INCOMPLETE")
        for rule in RULES:
            sid = rule["id"]
            if (self._entities.get(sid) != TARGET or sid not in self._origins
                    or sid not in self._timelines
                    or not all(self._source_ready(s) for s in rule["sources"])
                    or self._statements[sid]["sourceBindings"] != {
                        s: self._cards[s]["revision"] for s in rule["sources"]}):
                return self._reject("STALE_OR_INCOMPLETE_BINDINGS", sid)
        self._invalidate("brief_built")
        self._brief_counter += 1
        self._brief = {
            "status": "INTERNAL_DRAFT", "case": "Pipit Account Evidence Desk",
            "fictional": True, "entityId": TARGET, "asOf": AS_OF,
            "sourceRevision": self._source_revision, "briefRevision": self._brief_counter,
            "statements": [deepcopy(self._statements[r["id"]]) for r in RULES],
            "unknownRegister": [{"assertionId": r["id"], "unknown": r["unknown"],
                                 "resolved": False} for r in RULES],
            "omissions": [{"cardId": p, "reason": self._cards[p]["exclusionReason"],
                           "bodyPresent": False} for p in ("P08", "P09")],
            "sourceOriginMap": deepcopy(self._origins),
            "eventTimelines": deepcopy(self._timelines),
            "outreachReviewDocket": {"fixtureRecipient": "R-T01",
                "fixtureAction": "DO_NOT_CONTACT", "priorMarketingObjection": True,
                "subscriberClass": "UNKNOWN", "realPermission": "NOT_ASSESSED"},
            "limitations": "Finite fixture wording only; no semantic truth, actual account, rights or legal permission verification.",
            **deepcopy(FLAGS),
        }
        return deepcopy(self._brief)

    def record_review(self, reviewer_token, brief_revision, reason):
        held = self._guard()
        if held:
            return held
        if type(reviewer_token) is not str or reviewer_token != REVIEWER:
            return self._reject("REVIEWER_TOKEN_NOT_ALLOWED")
        if (not exact_int(brief_revision) or not self._brief
                or brief_revision != self._brief["briefRevision"]):
            return self._reject("BRIEF_REVISION_MISMATCH")
        if type(reason) is not str or reason not in REASONS:
            return self._reject("REVIEW_REASON_NOT_ALLOWED")
        self._current_export = None
        self._review = {"status": "ASSUMED_INTERNAL_REVIEW",
            "reviewerToken": REVIEWER, "reason": reason, "briefRevision": brief_revision,
            "sourceRevision": self._source_revision, "briefHash": canonical_hash(self._brief),
            "authority": "Code-owned teaching assumption only", **deepcopy(FLAGS)}
        self._audit.append({"event": "assumed_review_recorded", "briefRevision": brief_revision})
        return deepcopy(self._review)

    def export_internal_review_packet(self):
        held = self._guard()
        if held:
            return held
        if (not self._brief or not self._review
                or self._review["sourceRevision"] != self._source_revision
                or self._review["briefHash"] != canonical_hash(self._brief)):
            return self._reject("CURRENT_REVIEW_REQUIRED")
        packet = {"status": "INTERNAL_REVIEW_PACKET", "brief": deepcopy(self._brief),
                  "assumedReview": deepcopy(self._review), "outreachAuthorised": False,
                  "notForOutreach": True, "filesWritten": 0, **deepcopy(FLAGS)}
        self._current_export = deepcopy(packet)
        self._export_history.append({"briefRevision": self._brief["briefRevision"],
            "sourceRevision": self._source_revision, "packetHash": canonical_hash(packet),
            "currentlyEligible": True})
        return deepcopy(packet)

    def change_source_revision(self, card_id, revision):
        held = self._guard(allow_stopped=True)
        if held:
            return held
        if (type(card_id) is not str or card_id != "P11" or not exact_int(revision)
                or revision != 2 or self._source_revision != 1):
            return self._reject("SOURCE_CHANGE_NOT_ALLOWED")
        self._invalidate("P11_withdrawal")
        self._source_history.append(deepcopy(self._cards["P11"]))
        self._cards["P11"].update(deepcopy(WITHDRAWAL))
        self._source_revision = 2
        self._expected_hash = canonical_hash(self._data)
        self._opened.pop("P11", None)
        for store in (self._entities, self._origins, self._timelines, self._statements):
            store.pop("B08", None)
        return {"status": "SOURCE_CHANGED", "cardId": "P11", "revision": 2,
                "sourceRevision": 2, "invalidatedAssertions": ["B08"],
                "asOf": AS_OF, **deepcopy(FLAGS)}

    def stop_workflow(self, reason):
        held = self._guard(allow_stopped=True)
        if held:
            return held
        if type(reason) is not str or reason not in STOP_REASONS:
            return self._reject("STOP_REASON_NOT_ALLOWED")
        self._stopped = True
        self._invalidate("stopped:" + reason)
        return {"status": "STOPPED", "sourceRevision": self._source_revision, **deepcopy(FLAGS)}

    def resume_workflow(self, expected_revision):
        held = self._guard(allow_stopped=True)
        if held:
            return held
        if (not self._stopped or not exact_int(expected_revision)
                or expected_revision != self._source_revision):
            return self._reject("RESUME_REVISION_MISMATCH_OR_NOT_STOPPED")
        self._stopped = False
        self._invalidate("resumed_without_review")
        return {"status": "RESUMED", "sourceRevision": self._source_revision,
                "reviewRestored": False, **deepcopy(FLAGS)}

    def request_external_action(self, action_id):
        held = self._guard()
        return held if held else self._reject("EXTERNAL_ACTION_EXCLUDED")

    def learner_materials(self):
        held = self._guard()
        if held:
            return held
        return {
            "status": "LEARNER_MATERIALS", "case": "Pipit Account Evidence Desk",
            "asOf": AS_OF, "assertionWorksheet": [
                {"assertionId": r["id"], "challengedClaim": r["claim"],
                 "entityBinding": "", "sourceIds": [], "originNotes": "",
                 "timeline": [], "disposition": "", "wordingId": "", "unknown": ""}
                for r in RULES],
            "briefTemplate": "# Internal account evidence draft\n\nEntity: [fixture ID]\nAs-of: [date]\nStatements and passages: [complete worksheet]\nHistory: [dated events]\nHypotheses: [label and alternatives]\nUnknowns: [keep visible]\nOmissions: [reason]\nOutreach review: NOT_ASSESSED\nOwner and correction route: [teaching assumption]\n",
            "wordingChoices": [{"wordingId": r["wording"], "text": r["text"]}
                               for r in RULES] + [
                {"wordingId": "W08_WITHDRAWN", "text": WITHDRAWN_TEXT}] + [
                {"wordingId": r["bad"], "text": r["claim"]} for r in RULES],
            **deepcopy(FLAGS),
        }

    def state_snapshot(self):
        return {"sourceRevision": self._source_revision,
            "stopped": self._stopped, "statementCount": len(self._statements),
            "briefRevision": self._brief["briefRevision"] if self._brief else None,
            "reviewPresent": self._review is not None,
            "currentExportPresent": self._current_export is not None,
            "bodyReads": deepcopy(self._body_reads), "fixedFixtureReads": self._fixture_reads,
            "exportHistory": deepcopy(self._export_history),
            "sourceHistory": deepcopy(self._source_history), "audit": deepcopy(self._audit),
            **deepcopy(FLAGS)}


def solve_fixture(lab):
    """Teacher walkthrough, not an assessment or model run."""
    operations = [lab.list_source_cards()]
    operations += [lab.open_company_card(s, lab._cards[s]["revision"]) for s in READABLE]
    for rule in RULES:
        sid = rule["id"]
        operations.append(lab.bind_entity(sid, TARGET))
        operations.append(lab.trace_origin(sid, list(rule["sources"])))
        operations.append(lab.record_timeline(sid, list(rule["events"]), AS_OF))
        wording = "W08_WITHDRAWN" if sid == "B08" and lab._source_revision == 2 else rule["wording"]
        operations.append(lab.propose_statement(sid, wording))
    if any(r["status"] == "HOLD" for r in operations):
        raise FixtureError("Teacher walkthrough held")
    brief = lab.build_internal_brief()
    if brief["status"] != "INTERNAL_DRAFT":
        raise FixtureError("Teacher brief held")
    review = lab.record_review(REVIEWER, brief["briefRevision"], "evidence_checked")
    if review["status"] == "HOLD":
        raise FixtureError("Teacher review held")
    return lab.export_internal_review_packet()


def main():
    parser = argparse.ArgumentParser(description=__doc__)
    parser.add_argument("mode", choices=("worksheet", "template", "demo", "reset-probe"))
    args = parser.parse_args()
    lab = PipitLab()
    if args.mode == "demo":
        output = {"packet": solve_fixture(lab), "observedState": lab.state_snapshot()}
    elif args.mode == "reset-probe":
        output = lab.state_snapshot()
    else:
        material = lab.learner_materials()
        if args.mode == "template":
            print(material["briefTemplate"])
            return
        output = material
    print(json.dumps(output, ensure_ascii=False, indent=2))


if __name__ == "__main__":
    if hasattr(sys.stdout, "reconfigure"):
        sys.stdout.reconfigure(encoding="utf-8")
    main()

python · 366 lines
"""Runtime checks for the original four-file Pipit teaching lab."""
from copy import deepcopy
import importlib.util
import json
from pathlib import Path
import subprocess
import sys
import tempfile
import unittest

sys.dont_write_bytecode = True
from pipit_lab import (AS_OF, FLAGS, READABLE, REVIEWER, RULES, TARGET,
                       FixtureError, PipitLab, canonical_hash, solve_fixture)

HERE = Path(__file__).resolve().parent


class PipitTests(unittest.TestCase):
    def setUp(self):
        self.lab = PipitLab()

    def prepared(self, sid):
        rule = next(r for r in RULES if r["id"] == sid)
        self.lab.list_source_cards()
        for card in rule["sources"]:
            if card in READABLE:
                self.assertEqual(self.lab.open_company_card(card, self.lab._cards[card]["revision"])["status"], "LOCAL_CARD")
        self.assertEqual(self.lab.bind_entity(sid, TARGET)["status"], "FIXTURE_ENTITY_BOUND")
        self.assertEqual(self.lab.trace_origin(sid, list(rule["sources"]))["status"], "ORIGIN_TRACED")
        self.assertEqual(self.lab.record_timeline(sid, list(rule["events"]), AS_OF)["status"], "TIMELINE_RECORDED")
        return rule

    def hold(self, result, reason):
        self.assertEqual(result["status"], "HOLD")
        self.assertEqual(result["reason"], reason)
        state = self.lab.state_snapshot()
        self.assertFalse(state["reviewPresent"])
        self.assertFalse(state["currentExportPresent"])
        for key in FLAGS:
            self.assertIs(result[key], False)

    def test_01_complete_packet_is_internal_and_false_flags(self):
        packet = solve_fixture(self.lab)
        self.assertEqual(packet["status"], "INTERNAL_REVIEW_PACKET")
        self.assertTrue(packet["notForOutreach"])
        self.assertEqual(packet["filesWritten"], 0)
        self.assertEqual(len(packet["brief"]["statements"]), 9)
        for key in FLAGS:
            self.assertIs(packet[key], False)
            self.assertIs(packet["brief"][key], False)
            self.assertIs(packet["assumedReview"][key], False)

    def test_02_metadata_has_eleven_cards_no_bodies(self):
        rows = self.lab.list_source_cards()["cards"]
        self.assertEqual([r["id"] for r in rows], ["P%02d" % n for n in range(1, 12)])
        self.assertTrue(all("body" not in row for row in rows))

    def test_03_excluded_bodies_absent_from_fixture(self):
        for card in ("P08", "P09", "P10"):
            self.assertNotIn("body", self.lab._cards[card])
        self.assertNotIn("@", json.dumps(self.lab._data))
        self.assertNotIn("https://", json.dumps(self.lab._data))

    def test_04_personal_body_cannot_be_opened(self):
        solve_fixture(self.lab)
        self.hold(self.lab.open_company_card("P08", 1), "BODY_EXCLUDED_OR_ABSENT")

    def test_05_restricted_body_cannot_be_opened(self):
        self.hold(self.lab.open_company_card("P09", 1), "BODY_EXCLUDED_OR_ABSENT")

    def test_06_preference_metadata_has_no_body_route(self):
        self.hold(self.lab.open_company_card("P10", 1), "BODY_EXCLUDED_OR_ABSENT")

    def test_07_arbitrary_path_and_url_are_not_sources(self):
        for card in ("../account_cards.json", "https://example.test/source", "C:/private.txt"):
            with self.subTest(card=card):
                self.hold(self.lab.open_company_card(card, 1), "UNKNOWN_CARD")

    def test_08_bool_is_not_a_source_revision(self):
        self.hold(self.lab.open_company_card("P01", True), "SOURCE_REVISION_MISMATCH")

    def test_09_wrong_entity_invalidates_prior_export(self):
        solve_fixture(self.lab)
        self.hold(self.lab.bind_entity("B02", "WE-C02"), "TARGET_ENTITY_MISMATCH")
        self.assertEqual(self.lab.state_snapshot()["statementCount"], 8)

    def test_10_wrong_entity_source_can_be_classified_honestly(self):
        rule = self.prepared("B02")
        out = self.lab.propose_statement("B02", rule["wording"])
        self.assertEqual(out["disposition"], "WRONG_ENTITY")
        self.assertFalse(self.lab._origins["B02"]["sources"][0]["targetEntityMatches"])

    def test_11_registered_office_does_not_become_operating_site(self):
        self.prepared("B01")
        self.hold(self.lab.propose_statement("B01", "OPERATES_AT_OFFICE"), "WORDING_NOT_SUPPORTED")

    def test_12_dependent_origins_not_two_confirmations(self):
        rule = self.prepared("B04")
        trace = self.lab._origins["B04"]
        self.assertEqual(trace["distinctCompanyOrigins"], ["cancellation-update", "expansion-announcement"])
        self.assertEqual(trace["expansionConfirmationOrigins"], ["expansion-announcement"])
        out = self.lab.propose_statement("B04", rule["wording"])
        self.assertEqual(out["disposition"], "DEPENDENT_ORIGIN")

    def test_13_cancellation_required_in_expansion_source_set(self):
        self.lab.list_source_cards()
        self.lab.open_company_card("P03", 1)
        self.lab.bind_entity("B03", TARGET)
        self.hold(self.lab.trace_origin("B03", ["P03"]), "SOURCE_SET_MISMATCH")

    def test_14_cancelled_plan_cannot_use_current_wording(self):
        self.prepared("B03")
        self.hold(self.lab.propose_statement("B03", "EXPANSION_IS_CURRENT"), "WORDING_NOT_SUPPORTED")

    def test_15_mirror_confirmation_wording_is_held(self):
        self.prepared("B04")
        self.hold(self.lab.propose_statement("B04", "TWO_INDEPENDENT_REPORTS"), "WORDING_NOT_SUPPORTED")

    def test_16_budget_invention_is_held(self):
        self.prepared("B05")
        self.hold(self.lab.propose_statement("B05", "BUDGET_150000"), "WORDING_NOT_SUPPORTED")

    def test_17_buyer_quotation_is_held(self):
        self.prepared("B07")
        self.hold(self.lab.propose_statement("B07", "BUYER_QUOTE_VERIFIED"), "WORDING_NOT_SUPPORTED")

    def test_18_draft_is_not_accepted_factual_evidence(self):
        card = self.lab.open_company_card("P07", 1)
        self.assertFalse(card["acceptedAsFactualEvidence"])
        rule = self.prepared("B07")
        self.assertEqual(self.lab.propose_statement("B07", rule["wording"])["disposition"], "UNSUPPORTED_QUOTATION")

    def test_19_vacancy_does_not_prove_purchase(self):
        self.prepared("B06")
        self.hold(self.lab.propose_statement("B06", "VACANCY_PROVES_PURCHASE"), "WORDING_NOT_SUPPORTED")

    def test_20_past_notice_does_not_prove_open_procurement(self):
        self.prepared("B08")
        self.hold(self.lab.propose_statement("B08", "PROCUREMENT_OPEN_TODAY"), "WORDING_NOT_SUPPORTED")

    def test_21_objection_and_unknown_subscriber_remain_held(self):
        packet = solve_fixture(self.lab)
        docket = packet["brief"]["outreachReviewDocket"]
        self.assertTrue(docket["priorMarketingObjection"])
        self.assertEqual(docket["subscriberClass"], "UNKNOWN")
        self.assertEqual(docket["fixtureAction"], "DO_NOT_CONTACT")
        self.assertEqual(docket["realPermission"], "NOT_ASSESSED")

    def test_22_no_outreach_ready_wording(self):
        self.prepared("B09")
        self.hold(self.lab.propose_statement("B09", "OUTREACH_READY"), "WORDING_NOT_SUPPORTED")

    def test_23_unknown_register_never_resolves_missing_budget(self):
        brief = solve_fixture(self.lab)["brief"]
        self.assertEqual(len(brief["unknownRegister"]), 9)
        self.assertTrue(all(x["resolved"] is False for x in brief["unknownRegister"]))
        self.assertIn("Approved purchase budget", [x["unknown"] for x in brief["unknownRegister"]])
        self.assertEqual([x["cardId"] for x in brief["omissions"]], ["P08", "P09"])

    def test_24_incomplete_worksheet_cannot_build(self):
        self.prepared("B01")
        self.lab.propose_statement("B01", "W01")
        self.hold(self.lab.build_internal_brief(), "ASSERTIONS_INCOMPLETE")

    def test_25_uninspected_source_cannot_trace(self):
        self.lab.bind_entity("B01", TARGET)
        self.hold(self.lab.trace_origin("B01", ["P01"]), "SOURCE_NOT_INSPECTED")

    def test_26_duplicate_sources_are_not_corroboration(self):
        self.lab.bind_entity("B01", TARGET)
        self.lab.open_company_card("P01", 1)
        self.hold(self.lab.trace_origin("B01", ["P01", "P01"]), "SOURCE_SET_MISMATCH")

    def test_27_chronology_must_preserve_event_order(self):
        rule = self.prepared("B03")
        self.hold(self.lab.record_timeline("B03", list(reversed(rule["events"])), AS_OF), "NON_CHRONOLOGICAL_EVENTS")

    def test_28_retrieval_is_not_event_date(self):
        self.prepared("B03")
        events = self.lab._timelines["B03"]["events"]
        self.assertEqual([x["eventDate"] for x in events], ["2026-05-03", "2026-05-20"])
        self.assertEqual([x["retrievedAt"] for x in events], [AS_OF, AS_OF])
        self.hold(self.lab.record_timeline("B03", ["P03", "P05"], "2026-05-20"), "AS_OF_MISMATCH")

    def test_29_free_text_is_not_a_semantic_check(self):
        self.prepared("B05")
        self.hold(self.lab.propose_statement("B05", "The budget might be 150000"), "WORDING_NOT_SUPPORTED")

    def test_30_forged_reviewer_is_held(self):
        solve_fixture(self.lab)
        self.hold(self.lab.record_review("real-manager", 1, "evidence_checked"), "REVIEWER_TOKEN_NOT_ALLOWED")

    def test_31_review_version_and_bool_rejected(self):
        for revision in (True, 0, "1", 99):
            with self.subTest(revision=revision):
                solve_fixture(self.lab)
                self.hold(self.lab.record_review(REVIEWER, revision, "evidence_checked"), "BRIEF_REVISION_MISMATCH")

    def test_32_review_reason_is_bounded(self):
        solve_fixture(self.lab)
        revision = self.lab.state_snapshot()["briefRevision"]
        self.hold(self.lab.record_review(REVIEWER, revision, "legally-approved"), "REVIEW_REASON_NOT_ALLOWED")

    def test_33_revision2_invalidates_only_affected_statement_and_all_approvals(self):
        original = solve_fixture(self.lab)
        result = self.lab.change_source_revision("P11", 2)
        self.assertEqual(result["invalidatedAssertions"], ["B08"])
        state = self.lab.state_snapshot()
        self.assertEqual(state["statementCount"], 8)
        self.assertFalse(state["reviewPresent"])
        self.assertFalse(state["currentExportPresent"])
        self.assertFalse(state["exportHistory"][0]["currentlyEligible"])
        self.assertEqual(state["sourceHistory"][0]["revision"], 1)
        # A prior returned copy remains a historical snapshot; it cannot be recalled.
        self.assertEqual(original["brief"]["sourceRevision"], 1)
        self.hold(self.lab.export_internal_review_packet(), "CURRENT_REVIEW_REQUIRED")

    def test_34_old_card_revision_cannot_reopen(self):
        self.lab.change_source_revision("P11", 2)
        self.hold(self.lab.open_company_card("P11", 1), "SOURCE_REVISION_MISMATCH")

    def test_35_withdrawal_requires_new_wording_and_review(self):
        solve_fixture(self.lab)
        self.lab.change_source_revision("P11", 2)
        self.prepared("B08")
        self.hold(self.lab.propose_statement("B08", "W08"), "WORDING_NOT_SUPPORTED")
        self.prepared("B08")
        self.assertEqual(self.lab.propose_statement("B08", "W08_WITHDRAWN")["disposition"], "WITHDRAWN_NOTICE")
        brief = self.lab.build_internal_brief()
        self.assertEqual(brief["sourceRevision"], 2)
        self.assertEqual(brief["briefRevision"], 2)
        self.lab.record_review(REVIEWER, 2, "manual_review")
        packet = self.lab.export_internal_review_packet()
        self.assertEqual(packet["status"], "INTERNAL_REVIEW_PACKET")
        self.assertFalse(packet["outreachAuthorised"])

    def test_36_invalid_revision_changes_do_not_apply(self):
        for card, revision in (("P01", 2), ("P11", True), ("P11", 3)):
            with self.subTest(card=card, revision=revision):
                self.hold(self.lab.change_source_revision(card, revision), "SOURCE_CHANGE_NOT_ALLOWED")
                self.assertEqual(self.lab.state_snapshot()["sourceRevision"], 1)

    def test_37_stop_blocks_export_and_mutations(self):
        solve_fixture(self.lab)
        self.lab.stop_workflow("learner_pause")
        self.hold(self.lab.export_internal_review_packet(), "STOPPED")
        self.hold(self.lab.bind_entity("B01", TARGET), "STOPPED")

    def test_38_resume_current_revision_never_restores_review(self):
        solve_fixture(self.lab)
        self.lab.stop_workflow("source_change")
        self.lab.change_source_revision("P11", 2)
        self.hold(self.lab.resume_workflow(1), "RESUME_REVISION_MISMATCH_OR_NOT_STOPPED")
        self.assertTrue(self.lab.state_snapshot()["stopped"])
        resumed = self.lab.resume_workflow(2)
        self.assertEqual(resumed["status"], "RESUMED")
        self.assertFalse(resumed["reviewRestored"])
        self.hold(self.lab.export_internal_review_packet(), "CURRENT_REVIEW_REQUIRED")

    def test_39_handled_bad_retry_clears_ready_export(self):
        solve_fixture(self.lab)
        self.assertTrue(self.lab.state_snapshot()["currentExportPresent"])
        self.hold(self.lab.propose_statement("B05", "BUDGET_150000"), "WORDING_NOT_SUPPORTED")
        self.hold(self.lab.export_internal_review_packet(), "CURRENT_REVIEW_REQUIRED")

    def test_40_prebody_typeerror_does_not_enter_invalidation(self):
        solve_fixture(self.lab)
        before = self.lab.state_snapshot()
        with self.assertRaises(TypeError):
            self.lab.propose_statement("B05", "W05", unexpected_keyword=True)
        after = self.lab.state_snapshot()
        self.assertEqual(before, after)
        self.assertTrue(after["reviewPresent"])
        self.assertTrue(after["currentExportPresent"])
        self.assertEqual(self.lab.export_internal_review_packet()["status"], "INTERNAL_REVIEW_PACKET")

    def test_41_memory_corruption_fails_export_preflight(self):
        solve_fixture(self.lab)
        self.lab._cards["P01"]["body"] += " tampered"
        self.hold(self.lab.export_internal_review_packet(), "CORRUPT_EVIDENCE")

    def test_42_fixed_fixture_corruption_and_missing_file(self):
        # Local relocated four-file fixture, not a user-supplied runtime pathname.
        with tempfile.TemporaryDirectory(dir=HERE, prefix="pipit-test-") as tmp:
            root = Path(tmp)
            script = root / "pipit_lab.py"
            script.write_bytes((HERE / "pipit_lab.py").read_bytes())
            cards = root / "account_cards.json"
            cards.write_bytes((HERE / "account_cards.json").read_bytes())
            spec = importlib.util.spec_from_file_location("relocated_pipit", script)
            module = importlib.util.module_from_spec(spec)
            spec.loader.exec_module(module)
            relocated = module.PipitLab()
            module.solve_fixture(relocated)
            data = json.loads(cards.read_text(encoding="utf-8"))
            data["cards"][0]["body"] += " corrupt"
            cards.write_text(json.dumps(data), encoding="utf-8")
            self.assertEqual(relocated.export_internal_review_packet()["reason"], "CORRUPT_OR_MISSING_FIXTURE")
            with self.assertRaises(module.FixtureError):
                module.PipitLab()
            cards.unlink()
            with self.assertRaises(module.FixtureError):
                module.PipitLab()

    def test_43_caller_mutated_copy_does_not_change_session(self):
        packet = solve_fixture(self.lab)
        packet["brief"]["statements"][0]["text"] = "changed outside session"
        packet["outreachAuthorised"] = True
        fresh = self.lab.export_internal_review_packet()
        self.assertNotEqual(fresh["brief"]["statements"][0]["text"], "changed outside session")
        self.assertFalse(fresh["outreachAuthorised"])

    def test_44_external_actions_all_held(self):
        for action in ("email", "call", "scrape", "crm-update", "model", {"url": "https://example.test"}):
            with self.subTest(action=action):
                solve_fixture(self.lab)
                self.hold(self.lab.request_external_action(action), "EXTERNAL_ACTION_EXCLUDED")

    def test_45_new_object_resets_process_state(self):
        solve_fixture(self.lab)
        self.lab.change_source_revision("P11", 2)
        self.lab.stop_workflow("learner_pause")
        state = PipitLab().state_snapshot()
        self.assertEqual(state["sourceRevision"], 1)
        self.assertEqual(state["statementCount"], 0)
        self.assertFalse(state["stopped"])
        self.assertFalse(state["reviewPresent"])
        self.assertEqual(state["exportHistory"], [])

    def test_46_actual_child_process_resets(self):
        solve_fixture(self.lab)
        child = subprocess.run([sys.executable, "-B", str(HERE / "pipit_lab.py"), "reset-probe"],
                               capture_output=True, text=True, encoding="utf-8", check=True)
        state = json.loads(child.stdout)
        self.assertEqual(state["sourceRevision"], 1)
        self.assertEqual(state["statementCount"], 0)
        self.assertEqual(state["bodyReads"], [])
        self.assertFalse(state["currentExportPresent"])

    def test_47_emitted_worksheet_and_template_have_no_completed_answers(self):
        material = self.lab.learner_materials()
        self.assertEqual(len(material["assertionWorksheet"]), 9)
        self.assertTrue(all(x["disposition"] == "" and x["sourceIds"] == [] for x in material["assertionWorksheet"]))
        self.assertIn("Outreach review: NOT_ASSESSED", material["briefTemplate"])
        child = subprocess.run([sys.executable, "-B", str(HERE / "pipit_lab.py"), "template"],
                               capture_output=True, text=True, encoding="utf-8", check=True)
        self.assertIn("Internal account evidence draft", child.stdout)

    def test_48_no_network_or_write_apis_in_lab(self):
        import ast
        tree = ast.parse((HERE / "pipit_lab.py").read_text(encoding="utf-8"))
        imports = {alias.name.split(".")[0] for node in ast.walk(tree)
                   if isinstance(node, ast.Import) for alias in node.names}
        imports.update(node.module.split(".")[0] for node in ast.walk(tree)
                       if isinstance(node, ast.ImportFrom) and node.module)
        self.assertEqual(imports, {"__future__", "argparse", "copy", "datetime",
                                   "hashlib", "json", "pathlib", "sys"})
        attributes = {node.attr for node in ast.walk(tree) if isinstance(node, ast.Attribute)}
        self.assertFalse(attributes & {"write_text", "write_bytes", "urlopen", "connect", "send"})
        packet = solve_fixture(self.lab)
        self.assertEqual(len(self.lab.state_snapshot()["bodyReads"]), 8)
        self.assertEqual(packet["filesWritten"], 0)


if __name__ == "__main__":
    unittest.main(verbosity=2)

Assessment options

Keep learning

The complete workbook

Eight original chapters use an account-name collision, repeated announcement, cancellation, archived advertisement and withdrawn invitation to teach evidence-led account research. Worked paper exercises and a finite offline Pipit lab retain every challenged assertion and gap. The internal brief and outreach-review docket remain separate; no live research, model, real account or contact operation is performed.

  1. 01
    Start with an account question and an access boundary

    Define a narrow account decision before admitting sources or proposed uses.

    Read here · 2 exercises
  2. 02
    Resolve the company without building a personal dossier

    Bind statements to the right company and retain only task-relevant information.

    Read here · 2 exercises
  3. 03
    Make time visible instead of calling everything current

    Separate history, later updates and present unknowns at a stated decision date.

    Read here · 2 exercises
  4. 04
    Trace evidence origins and test the actual sentence

    Check exact propositions and origins instead of counting citations.

    Read here · 2 exercises
  5. 05
    Keep hypotheses useful and unknowns honest

    Turn a company observation into a testable research question while preserving missing evidence and permitted-data boundaries.

    Read here · 1 exercise
  6. 06
    Share the minimum brief and retain control of its use

    Use a six-component prompt and traceable output schema to preserve company evidence, hypotheses, unknowns and held actions.

    Read here · 1 exercise
  7. 07
    Separate a researched account from an outreach decision

    Review purpose, jurisdiction, channel and preferences separately from account evidence and an assumed internal review.

    Read here · 1 exercise
  8. 08
    Produce, challenge and recover a dated account brief

    Build the full internal packet, then rehearse a controlled withdrawal and recovery without restoring stale review or losing historical limits.

    Read here · 1 exercise

Also inside: a 10-point checklist, a glossary of 12 terms and 12 questions and answers to test yourself. 12 hands-on exercises, each with a worked answer at the back where the workbook gives one.

The PDF is free. We ask for your name and email before downloading. Read the privacy notice at the download form. Free to download and use for personal learning, study groups and inside your own team. Please do not resell the workbooks or republish them as your own. Link people to trust-agent.ai instead.

Get your free PDF workbook

Enter your name and email, then download the complete workbook. This form also works without JavaScript.

We privately record your name, email and request IP for this download. Read our privacy notice and retention policy.

Visit Mickai on LinkedIn

Newsletter confirmation does not affect your download. Following Mickai on LinkedIn is optional; this site does not verify followers.

Test yourself

Questions and answers

Why reject an exhaustive buyer profile as the first task?

It expands scope into unnecessary and speculative personal fields before identifying the company evidence needed for the decision.

What can P09 establish?

Only supplied restriction metadata and body absence, not any unread report finding.

Does a locator establish rights?

No. Location, supported content and permission for the intended action are separate checks.

Why is a shared name fragment insufficient?

It does not establish that WR-C01 and WE-C02 are the same entity or related.

What does registered office not prove?

An operating warehouse, site attendance or a place to visit.

Can information without a name identify someone?

Yes. Role and context may identify a person, so name removal alone is not anonymity.

Which four times differ?

Supplied event, publication, retrieval and decision as-of dates; missing fields stay absent.

What does P05 change?

It cancels this supplied P03 proposal, not every possible future expansion.

How many expansion origins do P03/P04 supply?

One shared announcement origin, with two records retained; P05 is a separate cancellation origin.

Why split propositions?

Support for one clause does not establish budget or preference added beside it.

Can a plausible hypothesis replace missing account evidence?

No. Keep the observation, alternatives and tentative question separate, and leave budget or buying intent unknown until appropriate evidence exists.

Does an absent budget mean the account has no budget?

No. Neither zero nor a plausible estimate is supported by the supplied packet. P07's number is authored challenge material.

Write as you go

My notes

Use this scratchpad for your own notes, working or answers as you read. Notes are saved in this browser on this device. Downloading makes a local copy; it does not send your notes to Mickai.

Learn → Practise → Demonstrate

Ready to put it into practice?

Take the scored exam when you feel ready. You do not need to download the workbook first. A score strictly above 80% earns a named, dated Mickai certificate with a verification page.

Take the official exam

Your name and email are collected at exam start. Certificates record an assessment result, not an accredited qualification or verified identity.

Learn the language

Key terms

Account question
A bounded company-related question intended to inform a stated internal planning decision.
Entity binding
A link between an assertion and its intended company token; fixture matching does not verify real identity.
Operating site
A place where operations occur; a recorded registered office does not by itself establish it.
Source origin
The underlying evidential source behind a publication; repeated reporting can share one origin.
Event date
The recorded time of the described event, distinct from publication and retrieval dates.
As-of date
The date bounding the packet interpretation; Pipit fixes it at1October2026.

6 of the workbook's 12 terms. The complete glossary is in the workbook.

Follow the evidence

Sources and checks

Facts last checked: .

These workbooks use AI assistance. See how the workbooks are made.

  1. Searching the Companies House registerCompanies House
  2. Business-to-business marketingInformation Commissioner's Office
  3. Collect information and generate leadsInformation Commissioner's Office
  4. Guidance on direct marketing using electronic mailInformation Commissioner's Office
  5. Data minimisation and accuracyInformation Commissioner's Office
  6. Right to be informedInformation Commissioner's Office
  7. Legitimate interestsInformation Commissioner's Office
  8. Exceptions to copyrightIntellectual Property Office; report companion DSIT/DCMS/IPO
  9. Artificial Intelligence Risk Management Framework: Generative Artificial Intelligence ProfileNational Institute of Standards and Technology
  10. CAN-SPAM Act: A Compliance Guide for BusinessFederal Trade Commission

Created by Mickarle Sean Junior Wagstaff-Irons - Micky Irons with the Mickai team. Published by Mickai LTD. Last updated 1 October 2026.

NextKeep going

Where to go next