%%{init:{"theme":"base","themeVariables":{"primaryColor":"#eef2ff","primaryTextColor":"#1e1b4b","primaryBorderColor":"#6366f1","lineColor":"#6366f1","clusterBkg":"#f8f9ff","clusterBorder":"#6366f1","titleColor":"#1e1b4b","edgeLabelBackground":"#f6f4ef","fontFamily":"system-ui,sans-serif","fontSize":"13px"}}}%%
stateDiagram-v2
[*] --> NOT_ACTIVE
NOT_ACTIVE --> ACTIVE: override set
ACTIVE --> EXPIRED: expiry reached
ACTIVE --> CLEARED: manual clear
EXPIRED --> NOT_ACTIVE: governance note written
CLEARED --> NOT_ACTIVE: governance note written
Chapter 3.6 Advanced Governance and State Management
Chapter 2.4 introduced manual_override_active as a boolean CRM property: when true, Workflow B skips the automated lifecycle transition and holds the contact in place. That flag is the entire Part II governance record. It captures state but nothing about the decision that produced it.
This chapter extends the boolean into a four-field governance record and adds the override lifecycle state machine the boolean cannot express. It also introduces policy versioning and approval chain patterns required for AI systems operating under compliance accountability. The extension is backward-compatible: the boolean is retained; four additional fields extend it.
Learning Objectives
After completing this chapter, you will be able to:
- Explain why a boolean
manual_override_activeflag is insufficient for production governance and describe the four failure modes it produces in a real deployment. - Implement the four extended governance fields (
override_expires_at,override_reason_code,override_authorized_by,override_policy_version) in the HubSpot Deal schema. - Design the override state machine (NOT_ACTIVE → ACTIVE → EXPIRED → CLEARED) and implement the transition logic in n8n, including the automated expiry check workflow.
- Apply the
deal_governance_tierproperty to scope governance intensity by deal risk level, configuring tier-appropriate override requirements for each tier. - Build the governance audit history entry format that makes every override decision fully reconstructible: who, when, why, under what policy, and for how long.
- Write a Mini-ADR documenting a governance calibration decision for the Meridian platform, with rationale that addresses compliance, operational, and engineering trade-offs.
3.6.1 Why Boolean Governance Breaks Down
Section 3.0.2 Structural Limits of Part II described the boolean governance limit: a single boolean cannot capture who set the override, why, when it expires, or which policy authorized it. At Part II scale a single advisory team, known contacts, supervised workflow the boolean is sufficient. At Part III scale, four failure scenarios make the boolean inadequate.
Failure 1 Permanent override accumulation
Without an expiry field, overrides set manually remain active indefinitely unless someone remembers to clear them. A contact placed under manual_override_active = true for an active negotiation six months ago remains suppressed today. The system cannot distinguish a current negotiation from a forgotten toggle.
Failure 2 Unattributed override
When a compliance audit asks who authorized suppression of the automated advisory path for a contact, manual_override_active = true provides no answer. Without override_authorized_by, the governance record cannot be attributed.
Failure 3 Policy version opacity
When policy guidelines change, contacts overridden under the old policy cannot be identified without override_policy_version. Re-review after a policy update requires querying contacts by the policy version that was active when the override was set.
Failure 4 No expiry enforcement
With only a boolean, Workflow B checks the flag but cannot check whether the flag has expired. Expiry requires a datetime field the boolean does not provide.
These are not documentation failures. They are system failures. A system that cannot distinguish a current negotiation hold from a six-month-old forgotten toggle is making routing decisions based on stale state. That stale state propagates into the observability metrics from Chapter 3.5 specifically into p6_last_advisory_path, where contacts suppressed by stale overrides never appear in the normal distribution.
3.6.2 Rich State Models
Rich State Models
The Part II governance model has two states:
NOT_ACTIVE ←→ ACTIVE
The Part III governance model has four states, each corresponding to a verifiable property combination on the Contact:
| State | Condition | System behavior |
|---|---|---|
NOT_ACTIVE |
manual_override_active = false |
Normal AI-driven transition |
ACTIVE |
manual_override_active = true AND override_expires_at > now() |
Override applied; AI transition suppressed |
EXPIRED |
manual_override_active = true AND override_expires_at ≤ now() |
System detects expiry; clears override; resumes normal transition |
CLEARED |
Override was manually cleared before override_expires_at |
Same result as NOT_ACTIVE; logged as intentional early clear |
Governance state is derived at execution time from existing properties, never stored as a separate field.
The state is not stored as a named property it is derived from the combination of manual_override_active and override_expires_at at execution time. This avoids a fifth property that could fall out of sync with the other two. A system that stores derived state in a separate property creates a consistency hazard. A system that derives state at execution time does not.
Never store derived state in a CRM property if you can compute it from properties that are already being maintained. manual_override_active + override_expires_at together define the governance state. A third override_state property would duplicate this derivation and create a consistency hazard.
3.6.3 The Four Extended Governance Fields
The Four Extended Governance Fields
The four fields added to the Part II boolean:
| Property | Type | Description | Example |
|---|---|---|---|
override_expires_at |
Datetime | When the override automatically expires | 2025-09-30T23:59:59Z |
override_reason_code |
Enumeration | Controlled vocabulary for why the override was set | ACTIVE_NEGOTIATION |
override_authorized_by |
Single-line text | Identity of the team member who set the override | sarah.chen@vap.com |
override_policy_version |
Single-line text | Policy version in effect when the override was set | POLICY-2025-Q3 |
All four are defined in the Lifecycle Governance property group alongside manual_override_active.
Reason code enumeration for the Vantage Advisory Partners domain:
| Code | Meaning | Typical expiry |
|---|---|---|
ACTIVE_NEGOTIATION |
Partner is actively in conversation; suppress automated follow-up | 30 days |
COMPLIANCE_HOLD |
Legal or compliance review pending | Until cleared manually |
PARTNER_REFERRAL |
Originated from a partner relationship requiring human handling | 60 days |
COOLING_PERIOD |
Recent rejection; avoid automated re-engagement | 90 days |
SYSTEM_REMEDIATION |
Data quality issue being resolved | 7 days |
The reason code enumeration is a governance decision, not a technical one. The appropriate codes depend on the domain and the team’s workflows. The codes above are calibrated for VAP. The Meridian Venture Partners capstone domain requires different codes (see ADR-6D Governance Calibration Rationale). A team that reuses VAP reason codes in a Meridian context will have an enumeration that does not match its actual override reasons. A team that calibrates reason codes to its domain will have an enumeration that is usable in a compliance audit.
3.6.4 Override Lifecycle
Override Lifecycle
The override lifecycle defines the valid transitions between states and the workflow actions that execute them.
Set transition (NOT_ACTIVE → ACTIVE):
A team member manually updates the Contact in HubSpot, setting: 1. manual_override_active = true 2. override_expires_at = current date + duration appropriate for the reason code 3. override_reason_code = the appropriate enum value 4. override_authorized_by = the team member’s email 5. override_policy_version = the current policy version string
Expiry transition (ACTIVE → EXPIRED → NOT_ACTIVE):
At the start of every Workflow B execution, before checking manual_override_active, a Check Expiry IF node computes:
override_expires_at < now() AND manual_override_active = true
If true: Workflow B clears all five governance fields, writes a governance audit note to the Contact (see 3.6.7 Governance History and Audit Trails), and continues with normal transition processing. The contact is in NOT_ACTIVE state for the remainder of the execution.
Manual clear transition (ACTIVE → CLEARED → NOT_ACTIVE):
A team member manually sets manual_override_active = false before expiry. The four extended fields should also be cleared at this point. The governance audit note records the manual clear with the team member’s action timestamp.
Do not clear only manual_override_active and leave the four extended fields populated. The next Check Expiry evaluation will compute the state from both manual_override_active AND override_expires_at. If manual_override_active = false but override_expires_at is still a future date, the derived state is NOT_ACTIVE (correct behavior) but the stale extended fields create a misleading governance record if they appear in a compliance audit.
3.6.5 Policy Versions
Policy Versions
override_policy_version captures which governance policy governed each override decision making contacts overridden under an old policy identifiable and re-reviewable after a policy change.
The override_policy_version field captures which version of the governance policy was in effect when the override was authorized. Policy versions follow a structured naming convention:
POLICY-{YYYY}-{QN}
Example: POLICY-2025-Q3 is the policy in effect for Q3 2025.
A policy version update occurs when governance rules change new reason codes added, expiry duration defaults revised, approval authority changed. When a policy update occurs:
- The new policy version string is communicated to all team members authorized to set overrides.
- Overrides set after the update carry the new version string.
- Overrides set under the previous version remain under that version string until they expire or are manually cleared.
- A HubSpot Contacts Search query filtered on
override_policy_version = POLICY-2025-Q2returns all contacts that require review under the new policy.
This is the mechanism that resolves Failure 3 from 3.6.1 Why Boolean Governance Breaks Down: contacts overridden under an old policy are identifiable and re-reviewable without manual tracking.
3.6.6 Approval Chains
Approval Chains
Part II governance has no approval chain any team member with HubSpot access can set manual_override_active = true. At Part III scale, some reason codes require authorization above the individual level.
Approval chain design for VAP:
| Reason code | Self-service | Requires approval |
|---|---|---|
ACTIVE_NEGOTIATION |
✅ Any team member | |
COOLING_PERIOD |
✅ Any team member | |
SYSTEM_REMEDIATION |
✅ Any team member | |
PARTNER_REFERRAL |
✅ Partner Relations lead | |
COMPLIANCE_HOLD |
✅ Compliance officer |
The approval chain is not enforced technically in Part III it is enforced by override_authorized_by, which records who authorized the override, and by periodic governance audits that verify the field matches the required approver for the reason code. Technical enforcement (requiring a second team member to confirm before the override is applied) is a capability for Phase 7 and beyond.
Record the approval chain in the governance properties, not in the workflow logic. The workflow checks manual_override_active it does not verify that the right person authorized it. That verification happens in governance audits, not at execution time. Mixing approval logic into the workflow creates a brittle coupling between business rules and workflow code.
3.6.7 Governance History and Audit Trails
Part II audit history (from Chapter 2.4 and the Part II Capstone) captures the automation trail: which workflow stage a contact passed through, when the lifecycle transition fired, what the AI returned. It does not capture governance decisions.
Part III governance history captures the governance trail: when an override was set, by whom, under what reason code, for how long, and when it was cleared (manually or by expiry). This is a separate record type.
Implementation: Write a HubSpot Note to the Contact record for each governance event. Notes are timestamped by HubSpot automatically and appear in the Contact activity timeline. The Note body uses a structured format:
GOVERNANCE EVENT: {event_type}
Timestamp: {ISO 8601}
Reason code: {override_reason_code}
Authorized by: {override_authorized_by}
Expires at: {override_expires_at}
Policy version: {override_policy_version}
---
{Free-text context, if any}
event_type values: OVERRIDE_SET, OVERRIDE_EXPIRED_AUTO_CLEARED, OVERRIDE_MANUALLY_CLEARED.
Part II vs. Part III governance record comparison:
| Field | Part II audit | Part III governance record |
|---|---|---|
| Automation events | ✅ Captured (workflow stage timestamps) | ✅ Inherited |
| AI advisory outputs | ✅ Captured (confidence, path, source) | ✅ Inherited via p6_ properties |
| Override state | ✅ Boolean recorded | ✅ Four-field record |
| Override reason | ❌ Not captured | ✅ override_reason_code |
| Override attribution | ❌ Not captured | ✅ override_authorized_by |
| Override expiry | ❌ Not captured | ✅ override_expires_at |
| Policy version | ❌ Not captured | ✅ override_policy_version |
| Event history | ❌ Snapshot only (current boolean value) | ✅ Time-series Notes on Contact |
The Part II audit trail answers: did the automation run correctly? The Part III governance record answers: was every control decision authorized, attributed, time-bounded, and documented? Compliance review requires both.
Reference Diagrams
Figure 3.6.1 Override State Machine
Figure 38.1 shows the four governance states and the transitions between them. State is derived at execution time from manual_override_active and override_expires_at it is not stored as a separate property.
Figure 3.6.2 Override Lifecycle
Figure 38.2 shows the full lifecycle of a single override from set through expiry or manual clear, with all five governance fields populated at each stage.
%%{init:{"theme":"base","themeVariables":{"primaryColor":"#eef2ff","primaryTextColor":"#1e1b4b","primaryBorderColor":"#6366f1","lineColor":"#6366f1","clusterBkg":"#f8f9ff","clusterBorder":"#6366f1","titleColor":"#1e1b4b","edgeLabelBackground":"#f6f4ef","fontFamily":"system-ui,sans-serif","fontSize":"13px"}}}%%
flowchart TD
A["t=0: Override SET manual_override_active=true expires_at=t+30d reason=Broker negotiation policy_version=2.1"]:::process --> B["t=15d: Workflow blocked ACTIVE override governance gate logs block"]:::fallback
B --> C{"Resolution path"}:::decision
C -->|"Path A auto-expiry"| D["t=30d: EXPIRES → NOT_ACTIVE governance note written"]:::process
C -->|"Path B manual clear"| E["t=22d: Ops clears override → CLEARED → NOT_ACTIVE governance note written"]:::process
classDef trigger fill:#dcfce7,stroke:#16a34a,color:#14532d,font-weight:600
classDef process fill:#eef2ff,stroke:#6366f1,color:#1e1b4b
classDef decision fill:#fef9c3,stroke:#ca8a04,color:#78350f,font-weight:600
classDef success fill:#d1fae5,stroke:#059669,color:#064e3b,font-weight:700
classDef fallback fill:#fee2e2,stroke:#dc2626,color:#7f1d1d
ADR-6D Governance Calibration Rationale
Mini-ADR 6.6-D | Context: The Meridian Venture Partners capstone domain processes 200–300 VC deal submissions per quarter. Meridian’s compliance team requires that all override decisions be attributed, time-bounded, and auditable under the firm’s LP reporting obligations. The governance property group from Chapter 3.6 must be calibrated for this domain before the capstone workflow is built.
Decision: Override expiry durations for Meridian are calibrated to deal cycle time rather than contact relationship duration:
DEAL_IN_DILIGENCE(60 days typical diligence cycle),COMPLIANCE_HOLD(manual-clear only no expiry until compliance officer clears),LP_RELATIONSHIP(90 days LP-referred deals require extended hold),REGULATORY_REVIEW(manual-clear only),DATA_QUALITY(7 days). All overrides require authorization by a Managing Director or above, recorded inoverride_authorized_by. Policy version follows the samePOLICY-{YYYY}-{QN}convention.
Rationale: Deal cycle times at Meridian are longer than contact relationship cycles at VAP. A 30-day expiry appropriate for VAP’s
ACTIVE_NEGOTIATIONcode would fire mid-diligence at Meridian. TheDEAL_IN_DILIGENCEcode is Meridian-specific and does not exist in the VAP enumeration reason code enumerations are domain-calibrated, not universal.
The manual-clear-only codes (
COMPLIANCE_HOLD,REGULATORY_REVIEW) exist because regulatory events have no predictable end date. Auto-expiry would restore AI-driven transitions while the hold is still legally required.
This ADR prepares for Capstone ADR-D, which formalizes governance calibration in the full seven-field format.
Practical Exercise 3.6 Extended Governance Property Group
Business Scenario
Vantage Advisory Partners has been using manual_override_active to suppress automated lifecycle transitions for contacts in active negotiations. The boolean works but when a compliance review asks who authorized a six-week-old suppression and why, no one can answer. Overrides set months ago remain active because there is no expiry mechanism.
The Problem
A boolean flag records current state but nothing about the decision that produced it. The team cannot identify which overrides were set under an old policy, attribute any override to an authorizing team member, or automatically expire overrides when the business condition ends.
The Architectural Solution
Extend the Lifecycle Governance property group with four fields that transform the boolean into an attributable, time-bounded, policy-versioned governance record. Add a Check Expiry node to Workflow B that enforces expiry on every execution and writes a time-series governance Note to the Contact record.
Updated Workflow
Figure 38.3 shows the updated Workflow B with the Check Override Expiry gate: expired overrides trigger a field-clear and governance note before the normal AI-driven transition path resumes.
%%{init:{"theme":"base","themeVariables":{"primaryColor":"#eef2ff","primaryTextColor":"#1e1b4b","primaryBorderColor":"#6366f1","lineColor":"#6366f1","clusterBkg":"#f8f9ff","clusterBorder":"#6366f1","titleColor":"#1e1b4b","edgeLabelBackground":"#f6f4ef","fontFamily":"system-ui,sans-serif","fontSize":"13px"}}}%%
flowchart TD
A(["Workflow B Trigger"]):::trigger --> B{"Check Override Expiry (IF node)"}:::decision
B -->|"expired"| C["Clear All 5 Governance Fields (HubSpot Update)"]:::process
C --> D["Write Governance Note OVERRIDE_EXPIRED_AUTO_CLEARED"]:::process
D --> E["Normal AI-Driven Transition Path"]:::process
B -->|"not expired"| E
classDef trigger fill:#dcfce7,stroke:#16a34a,color:#14532d,font-weight:600
classDef process fill:#eef2ff,stroke:#6366f1,color:#1e1b4b
classDef decision fill:#fef9c3,stroke:#ca8a04,color:#78350f,font-weight:600
classDef success fill:#d1fae5,stroke:#059669,color:#064e3b,font-weight:700
classDef fallback fill:#fee2e2,stroke:#dc2626,color:#7f1d1d
Step 1 Add the Four Properties to HubSpot
Purpose
Extend the Lifecycle Governance property group with the four fields that transform the boolean manual_override_active flag into a full governance record. These properties are the data foundation for every subsequent step in this practical without them, the Check Expiry node has no datetime field to compare, the governance Note has no reason code to record, and compliance review cannot identify who authorized an override or under which policy.
Operation Summary
| Property | Value |
|---|---|
| Location | HubSpot Property Settings → Lifecycle Governance group |
| Primary Function | Define four structured governance fields alongside manual_override_active |
| Input | Manual HubSpot property configuration |
| Output | Four new Contact properties available for workflow reads and writes |
Property Configuration Table
| Property | HubSpot Type | Configuration Notes |
|---|---|---|
override_expires_at |
Date and time | Must be datetime, not text required for n8n $now.toISO() comparison in Check Expiry |
override_reason_code |
Dropdown select | Add the five VAP reason codes from 3.6.3 The Four Extended Governance Fields as enum values |
override_authorized_by |
Single-line text | Accepts email address of the authorizing team member |
override_policy_version |
Single-line text | Default value: POLICY-2025-Q3; update when governance policy changes |
Engineering Rationale
override_expires_at must be created as a HubSpot datetime property not a text field because the n8n $now.toISO() comparison in the Check Expiry node requires a datetime type for the less-than comparison to evaluate correctly. A text field will not produce a meaningful datetime comparison. Creating it as text is the single most common setup error in this practical. If an existing override is not expiring when expected, verify the property type in HubSpot Settings before debugging the workflow logic.
Step 2 Add the Check Expiry Node to Workflow B
Purpose
Enforce automated override expiry on every Workflow B execution. Without this node, expired overrides remain active indefinitely contacts whose override duration has elapsed continue to have their AI-driven lifecycle transitions suppressed even though no current business reason exists. The Check Expiry node evaluates the governance state at execution time and clears the stale override before the existing manual_override_active check is reached.
Operation Summary
| Property | Value |
|---|---|
| Node Type | IF node (Check Override Expiry) |
| Position | First node in Workflow B before the existing manual_override_active IF node |
| Primary Function | Detect expired overrides and route to the auto-clear path |
| Input | Contact record with five governance fields |
| Output | Expired path (clears fields + writes Note) or Not-expired path (continues normally) |
Expiry Condition Logic
// IF node condition both must be true to trigger the expired path
manual_override_active === true
AND
override_expires_at < {{ $now.toISO() }}If both conditions are true (expired path), execute in sequence: 1. HTTP Request: HubSpot Update Contact set manual_override_active = false, clear all four extended fields. 2. HTTP Request: HubSpot Create Note on Contact body formatted per 3.6.7 Governance History and Audit Trails, event_type: OVERRIDE_EXPIRED_AUTO_CLEARED. 3. Continue execution to the normal AI-driven transition path.
If either condition is false (not expired), bypass the clear path and continue to the existing manual_override_active check.
Output Table
| Output | Description |
|---|---|
| Expired path | All five governance fields cleared; governance Note written; normal AI transition resumes |
| Not-expired path | No change to governance fields; existing manual_override_active check handles next decision |
Engineering Rationale
Governance state is derived at execution time from manual_override_active combined with override_expires_at it is not stored as a third named property. The Check Expiry node is the mechanism that makes this derivation actionable: when expiry is detected, the node transitions the contact from the EXPIRED derived state to NOT_ACTIVE by clearing both fields. Placing this node first in Workflow B ensures that no execution proceeds on a stale override. If expiry detection ran later in the workflow, the existing manual_override_active check would suppress the AI transition before expiry was evaluated.
Step 3 Add the Governance Note to the Manual Override Path
Purpose
Write a time-series audit record to the Contact’s activity timeline for every Workflow B execution that suppresses an AI lifecycle transition. The observability layer from Chapter 3.5 aggregates p6_ properties to measure system behavior but it does not record governance decisions. Without governance Notes, the audit trail shows only that a contact was suppressed; it does not record the reason, authorization, or policy that governed the suppression.
Operation Summary
| Property | Value |
|---|---|
| Node Type | HTTP Request |
| Method | POST |
| Endpoint | /crm/v3/objects/notes |
| Primary Function | Write structured governance event record to the Contact activity timeline |
| Trigger | When manual_override_active = true AND override_expires_at > now() (active override) |
| Output | HubSpot Note ID; Note appears in Contact activity timeline |
Request Payload
{
"properties": {
"hs_note_body": "GOVERNANCE EVENT: TRANSITION_SUPPRESSED\nTimestamp: {{$now.toISO()}}\nReason code: {{$json.override_reason_code}}\nAuthorized by: {{$json.override_authorized_by}}\nExpires at: {{$json.override_expires_at}}\nPolicy version: {{$json.override_policy_version}}"
},
"associations": [
{
"to": { "id": "{{$json.hs_object_id}}" },
"types": [{ "associationCategory": "HUBSPOT_DEFINED", "associationTypeId": 202 }]
}
]
}The Note body follows the structured format from 3.6.7 Governance History and Audit Trails. All five governance field values are captured in the Note at the time of the suppression event. HubSpot timestamps the Note automatically.
Request Field Table
| Field | Required | Description |
|---|---|---|
hs_note_body |
Yes | Structured governance event record with all five field values |
associations |
Yes | Links the Note to the Contact record for timeline visibility |
associationTypeId |
Yes | 202 is the HubSpot-defined association type for Contact → Note |
Output Table
| Output | Description |
|---|---|
| Note ID | HubSpot Note ID confirming the governance record was created |
| Timeline | Note appears in the Contact’s activity timeline with HubSpot’s auto-timestamp |
Engineering Rationale
A governance record that shows only current state that manual_override_active was true as of the last Workflow B execution cannot answer compliance questions about when the override was set, how long it was active, or who authorized it. The time-series Notes pattern is what converts the snapshot-only Part II record into the historical audit trail Part III governance requires. Each suppression event produces one Note; over the override’s active period, the Contact’s timeline accumulates a complete record of every execution that respected the override. This record survives even if the governance fields are later cleared.
Step 4 Test the Auto-Expiry Path
Purpose
Verify that the Check Expiry node correctly detects an expired override, executes the full auto-clear sequence, and writes the governance Note before Workflow B continues to the normal AI-driven transition path. This test must pass before activating the workflow for production contacts an undetected expiry means a contact remains suppressed indefinitely.
Operation Summary
| Property | Value |
|---|---|
| Node Type | Manual test execution |
| Test method | Set override_expires_at to a past date on a test contact; trigger Workflow B manually |
| Primary Function | Validate the expired path: field clear + Note write + AI transition resume |
| Input | Test contact with all five governance fields set; override_expires_at = yesterday |
| Output | Five fields cleared in HubSpot; governance Note on Contact; normal AI transition fires |
Test Setup
// Test contact governance field values to configure before running
manual_override_active: true
override_expires_at: // set to yesterday e.g., 2025-06-24T23:59:59Z
override_reason_code: "ACTIVE_NEGOTIATION"
override_authorized_by: "test.user@vap.com"
override_policy_version: "POLICY-2025-Q3"Set these values on the test contact in HubSpot directly, then trigger Workflow B manually using the n8n Test Workflow button.
Verification Checklist
| Verification Item | Expected Result |
|---|---|
| Check Expiry IF node routes to expired path | True branch executes |
manual_override_active in HubSpot after run |
false |
| All four extended fields in HubSpot after run | Cleared (empty) |
| Governance Note on Contact activity timeline | Visible with OVERRIDE_EXPIRED_AUTO_CLEARED and all five field values |
| Workflow B continues to AI-driven transition | AI assessment executes normally after expiry clear |
Engineering Rationale
Testing the expiry path against a deliberately expired contact is the only way to verify that the datetime comparison in the IF node evaluates correctly for your HubSpot property type and n8n expression syntax. A datetime comparison that silently fails evaluates to false the workflow routes to the not-expired path and the contact remains suppressed with no error. This failure is invisible without an explicit test because it produces no workflow error; it produces incorrect governance behavior.
Step 5 Test the Policy Version Check
Purpose
Verify that contacts overridden under a prior policy version are identifiable and queryable. This test confirms that override_policy_version is being written correctly and that the HubSpot Search filter for policy-versioned contacts works before the governance record is needed for a real compliance review.
Operation Summary
| Property | Value |
|---|---|
| Method | POST |
| Endpoint | /crm/v3/objects/contacts/search |
| Primary Function | Identify contacts carrying an old policy version string |
| Input | Filter on override_policy_version with a prior version value |
| Output | Array of contacts requiring re-review under the current policy |
Request Payload
{
"filterGroups": [
{
"filters": [
{
"propertyName": "override_policy_version",
"operator": "EQ",
"value": "POLICY-2025-Q2"
}
]
}
],
"properties": [
"hs_object_id",
"firstname",
"lastname",
"override_reason_code",
"override_authorized_by",
"override_expires_at",
"override_policy_version"
],
"limit": 100
}Test Sequence
// Step 1: Run the query expect zero results in a clean test environment
// If zero results: confirm override_policy_version is being written correctly
// Step 2: Manually set one test contact's override_policy_version to "POLICY-2025-Q2"
// Re-run the query expect exactly one result
// Step 3: Verify the result includes all governance fields for that contact
const results = $json.results;
// results.length should be 1; results[0].properties.override_policy_version should be "POLICY-2025-Q2"Output Table
| Output | Description |
|---|---|
| Zero results | Expected in a clean test environment no contacts under old policy |
| One result | Expected after manually setting one test contact to old policy version |
| Full record | All governance fields returned for each matching contact for audit review |
Engineering Rationale
The policy version check workflow is not exercised during normal Workflow B operations it runs on demand when a policy changes and re-review of older overrides is required. Testing it now, before any policy update actually occurs, confirms the query is correctly constructed and the override_policy_version field is populated on contacts where the override was set correctly. A policy version query that returns zero results when it should return several contacts is not a clean environment it is a data gap that makes the compliance re-review impossible when it is actually needed.
If Check Expiry does not fire on an expired override, verify that override_expires_at is stored as a HubSpot datetime property (not a text property). The n8n $now.toISO() comparison requires a datetime type. If the property was created as text, delete it and re-create it as date and time.
Deliverable: Four extended governance properties added to HubSpot. Workflow B with Check Expiry node. Auto-expiry path tested and verified. Governance Note written on expiry. Policy version query demonstrated.
Estimated time: 3–4 hours.
Production Consideration
The governance Note pattern is the most frequently skipped step in practice teams implement the four fields but omit the Note-writing on lifecycle events. Notes are required for the capstone’s compliance audit deliverable: without them, the governance record shows current state but not history. A contact whose override expired three months ago has no audit trail in a Notes-free implementation.
For teams operating under active compliance requirements, consider adding a weekly reconciliation query that checks for contacts where manual_override_active = true but override_authorized_by is empty indicating an override was set without completing the full five-field record. Flag these contacts in the Slack #vap-ops channel.
Discussion Questions
The governance state is derived from
manual_override_activeandoverride_expires_atrather than stored as a named property. What is the consistency risk of this approach, and under what condition would the derived state produce a wrong result?The approval chain for
COMPLIANCE_HOLDandPARTNER_REFERRALis recorded inoverride_authorized_bybut is not technically enforced by the workflow. A team member could setoverride_reason_code = COMPLIANCE_HOLDwith their own email inoverride_authorized_by. What is the minimum system change that would technically enforce the approval requirement without rebuilding the workflow?Mini-ADR 6.6-D calibrates expiry durations for Meridian Venture Partners based on deal cycle time. What additional domain information would you want before finalizing the
DEAL_IN_DILIGENCEexpiry at 60 days? What signal in the observability data from Chapter 3.5 would tell you the calibration was wrong?
Chapter Summary
The Part II manual_override_active boolean captures state but not the decision that produced it. The Part III four-field governance record override_expires_at, override_reason_code, override_authorized_by, override_policy_version transforms the boolean into an attributable, time-bounded, policy-versioned governance record.
Key Principle
A governance system that records only current state cannot answer who set an override, why, for how long, or under which policy. The four extended fields transform a boolean into a full governance record that survives compliance review.
Four states (NOT_ACTIVE, ACTIVE, EXPIRED, CLEARED) are derived at execution time from the combination of manual_override_active and override_expires_at. The Check Expiry node in Workflow B detects and auto-clears expired overrides on every execution. Governance Notes provide a time-series audit trail that the snapshot-only Part II record cannot.
Mini-ADR 6.6-D calibrates the governance model for the Meridian Venture Partners capstone domain, establishing domain-specific reason codes and expiry durations based on deal cycle time rather than the relationship-cycle calibration appropriate for VAP.
Transition to Chapter 3.7
Chapters 3.5 and 6.6 establish what can be measured (observability) and what can be controlled (governance). Neither answers how the system can be tested before a change goes live and how the effects of a prompt update, model change, or architecture modification can be verified in advance.
Chapter 3.7 introduces the testing and evaluation framework for AI systems: fixture sets, regression suites, and the evaluation workflow that uses the prompt version correlation from Prompt Version Correlation and Drift Rate as the pass/fail signal. The p6_prompt_version property written in Chapter 3.5 connects directly to the fixture-based evaluation approach in Chapter 3.7.
Key Takeaways
- The Part II boolean captures state but not the decision that produced it. Four failure scenarios permanent accumulation, unattributed overrides, policy opacity, and no expiry enforcement make the boolean insufficient at Part III scale.
- The Part III governance state (
NOT_ACTIVE,ACTIVE,EXPIRED,CLEARED) is derived frommanual_override_activecombined withoverride_expires_atat execution time. It is not stored as a separate property. - The four extended fields are:
override_expires_at(datetime),override_reason_code(enumeration),override_authorized_by(text),override_policy_version(text). - The Check Expiry node runs at the start of every Workflow B execution. When expiry is detected, the workflow auto-clears all five fields, writes a governance Note, and continues to the normal AI-driven transition path.
- Governance Notes provide a time-series audit trail on the Contact. Each override lifecycle event set, auto-expired, manually cleared produces a structured Note with all five governance field values at that point in time.
- Reason code enumerations and expiry durations are domain-calibrated, not universal. VAP calibrates to relationship cycle time; Meridian calibrates to deal cycle time. Mini-ADR 6.6-D prepares for Capstone ADR-D.
End of Chapter 3.6 Advanced Governance and State Management