Strategy data model
Rolling out
The strategy module is being built and rolled out now. Shapes below follow spark-contracts (strategy contracts) and may still gain fields.
Steps
STRATEGY_STEPS = business, competitors, customers, positioning, goal, budget, plan. The client UI adds an eighth screen, Check & approve, which stores no answer of its own.
| Step | Answer shape (simplified) |
|---|---|
business | offers[] (name, price, unit), areas[], channels[] |
competitors | competitors[] (name, address, rating, website, maps link, note), swot (strengths, weaknesses, opportunities, threats) |
customers | segments[], each with a persona (age range, jobs, pains, gains) |
positioning | promise, proofs[] |
goal | unit (leads, calls, bookings, sales, visits, followers, messages), target, byDate, basis |
budget | monthly, weeklyCap, currency (EGP, SAR, USD), split[] per platform |
plan | mix[] (product, price, place, promotion, people, process, physical evidence), funnel[] (platform, stage, objective, KPI), firstMonth[] (weeks 1–4) |
Numbers in strategy content are safe integers: money in whole currency units, shares in integer percent, ratings in tenths, click-through rate in basis points.
Provenance: Sourced<T>
Every answer value is wrapped:
interface Sourced<T> {
value: T;
source: FieldSource;
confirmed: boolean;
ref?: { what: string; at: string; url?: string };
}FieldSource is one of:
| Source | Meaning | Needs confirmation |
|---|---|---|
client | typed by the client, or a suggestion the client edited | no |
account | read from a connected platform or Spark data (ref says what and when) | no |
lookup | found by the competitor lookup (ref says where and when) | no |
benchmark | an estimate from a published source (ref carries source and date) | no |
ai_suggestion | an AI suggestion the client accepted unchanged | no |
ai_assumption | the client chose "let AI decide" | yes |
account_manager | added by a Spark account manager | yes |
NEEDS_CONFIRMATION = ai_assumption, account_manager. Approval is refused while any such field is unconfirmed.
Versions
- One strategy head per organization: working language, current draft version, approved version, review dates, staff notes.
- Numbered versions with status
draft,approved,supersededorwithdrawn, and an origin (start,edit,review). The database enforces at most one draft and one approved version per organization. - A version tracks
completedsteps andstalesteps (a later step pointing at the earlier step whose change made it stale). - Editing an approved version creates a new draft based on it (
basedOn). The approved version stays in force until the new one is approved, then becomessuperseded.
Approval and the hash
Approve runs these guards in order: the version is the draft → every step complete → none stale → no unconfirmed field → content hash matches.
- The hash is
hashCanonical(content)fromspark-contracts: canonical JSON (sorted keys, safe integers only) hashed. The client sends the hash it read; if the content changed since, approval answersapproval_stale. - Approval records who, when and the reading language. The hash covers the working-language text only.
Link to launch cells
On approval the launch cells situation_analysis, stp, positioning, personas_jtbd, marketing_mix, smart_goals, budget_cap, plan_signoff (stage 4) and funnel_objectives, kpi_targets (stage 6) become done with method plan and the version number. This sync is idempotent and re-runs on every check. Superseding keeps them done; withdrawing with no replacement returns synced cells to not_started. If the monthly review is more than 60 days overdue they become needs_fix.
Fair use
Per-organization counters cap AI re-drafts per step per day, recommendation refreshes, review drafts per month and competitor lookups per day. A failed downstream call refunds its slot. Hitting a cap returns a notice (quota_reached), never an error that blocks the client.