Corrective Action: V Bypassed Ledger, Created Duplicate HubSpot Properties During All-Hands Publishing

Corrective Action: V Bypassed Ledger, Created Duplicate HubSpot Properties During All-Hands Publishing

Date: March 14, 2026 Category: Governance Bypass / Verification Failure Impact: 28 ah_* properties exist in HubSpot when 18 should. 5 semantic duplicate pairs across 2 property groups. Data written to wrong properties. 2 identical display labels visible in portal settings. Resolution Time: In progress — identified ~30 minutes after publishing


Incident

What Happened

During the March 14 All-Hands, V (operating as the /all-hands command) created HubSpot Listing records and populated them with structured ah_* properties. V bypassed Ledger entirely — using ToolSearch to load MCP write tools directly and executing property creation and batch updates without validation. After context compaction, V queried the March 14 record for March 9 property names, received null values, and incorrectly interpreted this as "these properties don't exist." V then created 10 new properties that semantically duplicate the existing March 9 schema.

Timeline

Time Event
~3:57am CT V creates March 14 All-Hands Listing record 539378841228 via direct MCP call (bypassing Ledger)
~4:00am CT Chris notes this is NOT the first All-Hands — March 9 records exist (538012103309, 538078631293)
~4:05am CT V reads March 9 records, acknowledges error, proposes delta framing
~4:07am CT Chris notes March 9 records lack summarized ah_* data, suggests structured property group
~4:08am CT V queries March 14 record for March 9 property names (ah_opening, ah_state_of_org, etc.) — all return null
~4:08am CT V misinterprets null values as non-existent properties. Designs new schema with different names.
~4:09am CT V creates 15 properties via hubspot-create-property (direct MCP, not Ledger). 5 hit 409 (already exist). 10 created.
~4:10am CT V batch-updates all 3 All-Hands records with new property names
~4:15am CT V updates listing.json property-index to match new properties (overwriting March 9 schema documentation)
~4:19am CT Chris shows HubSpot screenshots: 28 properties visible, duplicates across two groups

Root Cause

Compound failure with three contributing causes:

1. V Bypassed Ledger for All Write Operations

The /all-hands command runs as V. V used ToolSearch to load mcp__hubspot__hubspot-batch-create-objects and other write tools directly, then executed creates and updates without spawning Ledger.

This violates skills/enforcement/vf-platform-context.md lines 109-125:

"All HubSpot write operations go through the Ledger agent. No other agent writes to HubSpot directly."

Ledger's startup protocol reads listing.json and validates every property name before writing. Had Ledger handled the property creation, it would have:

  • Read the existing property-index (which documented the March 9 schema)
  • Flagged the naming conflicts (ah_ops_report vs existing ah_operations_org)
  • Used existing properties instead of creating duplicates

2. Null Data Misinterpreted as Non-Existent Properties

After context compaction, V needed to determine which ah_* properties existed. V queried the March 14 record (newly created, mostly empty) for March 9 property names. All returned null.

In HubSpot, null means "this property has no value on this record." It does NOT mean "this property doesn't exist." Every property returns null when empty. This is fundamental HubSpot API behavior.

V treated null as evidence of non-existence and proceeded to create new properties.

3. No Pre-Creation Property Discovery

Before creating properties, V should have queried HubSpot for all existing properties with the ah_ prefix — either via the property API or by reading a record with all known ah_* property names from the property-index. V did neither. V checked only 5 specific names (via a sample record query), found they existed, then created 10 more without checking for overlap.

Category: Governance Bypass

This is the same pattern as "V IS A TEAM LEADER, NOT A SOLO OPERATOR" (corrected March 9, 2026). V operated as a solo writer instead of delegating to the specialist (Ledger) that was specifically built to prevent this class of error. The All-Hands command predates Ledger (March 9 vs March 12), but the March 14 run should have used Ledger — the enforcement was already in place.


Current State (What Exists in HubSpot)

March 9 Properties (Document Metadata group) — 18 properties

Internal Name Type Display Label Status
ah_opening textarea Opening Exists, no data on any record
ah_state_of_org textarea State of the Org Exists, no data
ah_growth_dashboard textarea Growth Dashboard Exists, no data
ah_celebrations textarea Celebrations & First Runs Has data (populated Mar 14)
ah_operations_org textarea Operations Org (V) Exists, no data
ah_customer_org textarea Customer Org (Sage) Exists, no data
ah_finance_org textarea Finance Org (Pax) Exists, no data
ah_cross_functional textarea Cross-Functional Exists, no data
ah_advisory_committee textarea Advisory Committee Exists, no data
ah_cross_org_themes textarea Cross-Org Themes Has data (populated Mar 14)
ah_dormant_connections textarea Dormant Connections Has data ("8" written as text)
ah_open_requests textarea Open Requests Has data ("14" written as text)
ah_what_to_watch textarea What to Watch Exists, no data
ah_closing textarea Closing Exists, no data
ah_meeting_type dropdown Meeting Type Exists, no data
ah_agent_count number Total Agents Exists, no data
ah_agents_active number Active Agents Exists, no data
ah_first_runs number First Runs Exists, no data

March 14 Properties (Custom Listing Information group) — 10 new

Internal Name Type Display Label Duplicates
ah_executive_summary textarea Executive Summary New (no March 9 equivalent)
ah_ops_report textarea Operations Org Report ah_operations_org
ah_customer_report textarea Customer Org Report ah_customer_org
ah_finance_report textarea Finance Org Report ah_finance_org
ah_watch_items textarea What to Watch ah_what_to_watch (same label!)
ah_total_agents number Total Agents ah_agent_count (same label!)
ah_ops_count number Operations Org Count New
ah_customer_count number Customer Org Count New
ah_finance_count number Finance Org Count New
ah_idle_count number Idle/Unexercised Count New
ah_producing_value_count number Producing Value Count New

Duplicate Pairs (5)

March 9 Property March 14 Property Same Label?
ah_operations_org ah_ops_report No (similar)
ah_customer_org ah_customer_report No (similar)
ah_finance_org ah_finance_report No (similar)
ah_what_to_watch ah_watch_items YES — both show "What to Watch"
ah_agent_count ah_total_agents YES — both show "Total Agents"

Fix Required

Phase 1: Consolidate Schema (Ledger)

Decide which properties to keep. The March 14 set has better structure (per-org numeric counts, cleaner naming). The March 9 set has more granular sections (opening, closing, growth dashboard, advisory committee, cross-functional). The target schema should combine both:

Keep from March 9 (unique sections):

  • ah_opening, ah_closing, ah_state_of_org, ah_growth_dashboard
  • ah_cross_functional, ah_advisory_committee
  • ah_meeting_type, ah_agents_active, ah_first_runs

Keep from March 14 (better alternatives):

  • ah_executive_summary (new, no March 9 equivalent)
  • ah_ops_report over ah_operations_org (already has data)
  • ah_customer_report over ah_customer_org (already has data)
  • ah_finance_report over ah_finance_org (already has data)
  • ah_watch_items over ah_what_to_watch (already has data)
  • ah_total_agents over ah_agent_count (already has data)
  • ah_ops_count, ah_customer_count, ah_finance_count, ah_idle_count, ah_producing_value_count (new, no March 9 equivalent)

Delete (duplicates with no data):

  • ah_operations_org (replaced by ah_ops_report)
  • ah_customer_org (replaced by ah_customer_report)
  • ah_finance_org (replaced by ah_finance_report)
  • ah_what_to_watch (replaced by ah_watch_items)
  • ah_agent_count (replaced by ah_total_agents)

Move ah_dormant_connections and ah_open_requests data:

  • These are March 9 textareas but had number strings ("8", "14") written to them
  • Keep as textareas (March 9 definition) — they hold descriptive content on March 9 records
  • The numeric aspect is captured by ah_idle_count and other metrics

Phase 2: Update Property Index

Rewrite the all_hands section of listing.json to reflect the consolidated 23-property schema.

Phase 3: Populate March 9 Records

Backfill the surviving March 9 section properties on records 538012103309 and 538078631293.


Prevention Measures

Rules Added

Layer File Rule
Critical Lessons memory/MEMORY.md NEVER bypass Ledger for HubSpot writes — including property creation. V is Ledger's leader, not Ledger's replacement. The /all-hands command, /meeting-prep, and every slash command that writes to HubSpot must delegate to Ledger.
Critical Lessons memory/MEMORY.md HubSpot null values mean "no data on this record" — NOT "property doesn't exist." Before creating any property, query the properties API or read the property-index. Never infer property existence from record data.
Self-Correction skills/enforcement/vf-self-correction.md Detection trigger: If V (or any agent) is about to call ToolSearch to load hubspot-batch-create-objects or hubspot-create-property directly, STOP. Spawn Ledger instead.
Enforcement skills/enforcement/vf-platform-context.md Already documented (lines 109-125). Enforcement was correct; execution violated it.
All-Hands Command .claude/commands/all-hands.md Add explicit instruction: "HubSpot publishing MUST go through Ledger"

Detection Triggers

If V catches itself loading HubSpot write tools via ToolSearch during a slash command execution, that is a governance bypass. The correct pattern is always:

Task(subagent_type: "ledger", prompt: "Create/update these records...")

Lessons

The gateway pattern only works if the gateway is actually used. Ledger was built 2 days before this All-Hands (March 12) and the enforcement was written into vf-platform-context.md. But the All-Hands command, which predates Ledger, was never updated to route through it. Every slash command that touches HubSpot needs an explicit Ledger delegation step — not just documentation saying "writes go through Ledger," but actual code in the command definition that spawns Ledger.

The deeper lesson: null is not absence. In HubSpot (and most APIs), a null property value and a non-existent property are different things. When checking whether schema exists, query the schema — not the data.


Related Incidents

Date Incident Pattern
2026-03-09 "V IS A TEAM LEADER, NOT A SOLO OPERATOR" V doing work that should be delegated to specialists
2026-03-11 Sanity write context loss Post-compaction amnesia leading to redundant/wrong operations
2026-03-12 HubSpot Beginners course integrity Writing to HubSpot without verifying schema exists
2026-03-12 ToolSearch mandatory prerequisite MCP tools must be loaded before use — governance around tool access