archive
Withdrawn v1 resolution contract
A public AppStore2031 research record. The readable view is generated without changing the preserved source.
Open the exact Markdown sourceResearch boundary: Observed evidence, inference, scenario and fictional forecast claims retain the labels used in the source record.
Forecast contract — withdrawn method v1 record
Withdrawn and superseded. This file documents the first continuity-led method only. It is retained so readers can audit what failed and why, but it is not the active AppStore2031 method and must not be used to generate, validate, rank or publish the replacement forecast. The active method is documented in
FORECAST_METHOD_V2.md.
Historical status at withdrawal: unfrozen research draft; never published.
This document defined the claims proposed for method v1, how those claims would have been produced, and how they would have been resolved. No edition was published under this contract. The text below is preserved in its historical form so its assumptions can be inspected.
Credibility commitment
AppStore2031 does not claim to know which fictional brands will exist. It makes dated, source-linked, probabilistic claims about app archetypes, categories, and rankings—and publishes in advance how those claims can be proved wrong.
The method draws on the OECD Strategic Foresight Toolkit, the UK Government's Futures Toolkit, Metaculus resolution guidance, Metaculus proper-scoring guidance, and Apple's current category definitions.
These are external standards, not evidence that any individual forecast is correct. In particular, scenarios are plausible futures used to challenge a forecast; they are not predictions in their own right.
1. Frozen forecast question
Once closed, the first edition answers:
As assessed at the edition manifest's exact
asOftimestamp, what categories and app archetypes will occupy the Top Free iPhone charts during July 2031 across the defined regional storefront basket?
The edition manifest must freeze these fields before app generation:
forecastId: asf-2031-v1
methodVersion: 1
forecastHorizon: 2031
asOf: null while researching; exact ISO-8601 UTC time assigned by seal preparation
resolutionWindow:
start: 2031-07-01
end: 2031-07-31
chartFamily: top-free-iphone-apps
platform: apple-iphone-app-store
marketModel: coverage-balanced-global-composite-v1
probabilityFloor: 0.01
probabilityCeiling: 0.99
forecastCloseForScoring: 2031-06-30T23:59:59Z
rankModel:
id: joint-rank-v3
drawsPerStorefront: 20000
minimumDrawsPerStorefront: 10000
Both the edition's evidence cutoff (treated as ending at 23:59:59 UTC)
and its seal-assigned exact asOf timestamp must be no later than
forecastCloseForScoring. Validation and seal preparation fail closed after
that instant, preventing an edition from incorporating July 2031 resolution
outcomes and then presenting itself as a pre-outcome forecast.
The target date shown to readers is 31 July 2031. Resolution uses the full calendar month so that a one-day promotion or news event cannot determine the result.
2. What is forecast and what is fictional
The fictional app name, developer, icon, screenshots, rating, reviews, and store description are educational scenario storytelling. They must be marked as fictional wherever they appear; a footer-only disclaimer is insufficient.
The resolvable unit is an app archetype: a bounded combination of user job, required capabilities, delivery modality, business model, likely provider type, and adoption mechanism. Fictional brand names are never scored.
Each archetype must:
- Define three to seven observable required capabilities.
- Define distinguishing capabilities and explicit disqualifiers.
- Be distinguishable from every other archetype in its category.
- Be applicable by a reviewer who cannot see its rank or rationale.
- Be narrowed if more than three materially different current apps already satisfy its full definition.
- Differ from neighbouring predictions by more than fictional branding.
One realised app may match at most one forecast archetype in a category, and
one archetype may receive at most one realised match. Resolution uses the
blind mapping process in BACKTEST.md.
3. Geographic contract
Apple does not publish a single global chart. The primary ranking is therefore a coverage-balanced global composite. It measures cross-market reach, not worldwide downloads, revenue, population, or installed-base share.
The proposed lens basket is frozen only after every underlying storefront has passed a chart-data availability check:
| Lens | Proposed storefronts |
|---|---|
| United States | us |
| Mainland China | cn |
| European Union | de, fr, it, es, nl, pl, se |
| Japan | jp |
| South Korea | kr |
| India | in |
| United Kingdom | gb |
| Southeast Asia | sg, id, my, th, vn, ph |
| Taiwan | tw |
| Gulf technology economies | ae, sa |
| Latin America | br, mx |
| Africa | za, ng, ke |
Each lens has equal weight in the global composite. Storefronts inside a multi-country lens also have equal weight. A storefront may be removed or replaced only before the first edition freezes, for a documented data- availability reason. The final basket, weights, failed availability checks, and decisions are public.
Forecasts are authored and resolved at the storefront level. A multi-country lens is a reporting and weighting group, not another binary or ordinal outcome. Its coverage, joint Top-10 probability, expected points, and proper score are equal-weight means across its valid storefronts; its conditional rank distribution uses the existence-weighted formula in section 5. Those mixtures describe a uniformly sampled storefront within the lens; they are not scored as if the lens had one realised rank. The global composite first averages storefront values inside each lens and then averages the twelve lens values, preventing a seven-storefront EU lens from receiving seven times the weight of Japan.
A separate market-size-weighted view may be added later only if a credible, dated source for iPhone installed base is available. It is a secondary view and may not replace or rewrite the coverage-balanced result.
Global research coverage
Every material cross-global driver must show evidence coverage from:
- The United States.
- Mainland China.
- The European Union.
- At least one of Japan or South Korea.
- At least one of India or Southeast Asia.
- Any additional region materially affected by the claim.
Regional claims should prefer local primary sources. Original language and translation method must be recorded. A government plan is evidence of intent, regulation, or investment, never evidence that its promised outcome occurred.
4. Category contract
For a storefront, a category exists when Apple, or a successor first-party iPhone storefront explicitly named in the edition's fallback rules, presents it as a top-level browse category with an independently ranked Top Free chart during at least 16 days of the July 2031 resolution window.
Every current Apple category must be assigned a fate. Every proposed category must have a distinct discovery purpose and ten non-duplicate archetypes. A theme that cannot support ten archetypes is a collection or trend, not a category.
type CategoryFate =
| "retained"
| "renamed"
| "merged"
| "split"
| "retired";
interface CategoryForecast {
id: string;
name: string;
status: "current" | "proposed-new";
definition: string;
inclusionCriteria: string[];
exclusionCriteria: string[];
currentAppleCategoryIds: string[];
fate?: CategoryFate;
successorCategoryIds: string[];
existenceProbabilityByStorefront: Record<StorefrontId, number>;
existenceCalibrationByStorefront: Record<StorefrontId, {
lensId: LensId;
probability: number;
nearestLowerAnchor: number;
nearestUpperAnchor: number;
sourceIds: string[];
rationale: string;
upwardCondition: string;
downwardCondition: string;
}>;
lensCoverageSummaries: Record<LensId, number>;
globalCoverageScore: number;
driverIds: string[];
claimIds: string[];
strongestCountercase: string;
signalIds: string[];
}
Each lensCoverageSummaries value is the equal-storefront mean inside that
lens. The globalCoverageScore is the equal-lens mean of those summaries: an
expected coverage index, not the probability that a majority of lenses will
contain the category, and it is not scored as a binary probability.
Categories at or above 0.50 appear in the primary forecast storefront and
contain exactly ten ranked apps. Lower-coverage candidates remain visible in
the category watchlist. Current categories below the threshold remain visible
in the retired/merged account. The number of categories is an output of the
research and is never fixed in advance.
Category-existence probabilities are scored separately for each storefront, then scores are macro-averaged within lenses and across lenses. The site may report realised coverage as a descriptive global outcome, but it does not publish or score a global-majority probability unless a later method version adds a frozen joint cross-storefront model.
5. App and rank schema
type RankBand =
| "rank-1"
| "rank-2-3"
| "rank-4-5"
| "rank-6-10"
| "outside-top-10";
interface RegionalRankForecast {
categoryExistsProbability: number;
conditionalRankPmf: Record<RankBand, number>;
jointTop10Probability: number;
expectedTop10Points: number;
jointModelRef: string;
}
interface LensRankSummary {
categoryCoverageScore: number;
conditionalRankPmf: Record<RankBand, number>;
jointTop10CoverageScore: number;
expectedTop10Points: number;
storefrontIds: StorefrontId[];
}
interface AppForecast {
id: string;
editionId: string;
categoryId: string;
fiction: {
name: string;
subtitle: string;
description: string;
developerName: string;
iconAsset: string;
rating: number;
imaginedReviews: Array<{
title: string;
body: string;
stars: number;
fictionalDisclosure: true;
}>;
fictionalDisclosure: true;
};
archetype: {
name: string;
userJob: string;
requiredCapabilities: string[];
distinguishingCapabilities: string[];
disqualifiers: string[];
deliveryModality: string;
businessModel: string;
likelyProviderType: string;
adoptionMechanism: string;
incumbentContinuity:
| "existing-incumbent-evolves"
| "new-entrant"
| "platform-native"
| "unknown";
};
forecast: {
byStorefront: Record<StorefrontId, RegionalRankForecast>;
lensSummaries: Record<LensId, LensRankSummary>;
globalExpectedTop10Points: number;
globalTop10CoverageScore: number;
displayedGlobalRank: number;
typicalStorefrontRankBand: RankBand[];
};
reasoning: {
referenceClass: string;
baseRate?: number;
positiveUpdates: EvidenceUpdate[];
negativeUpdates: EvidenceUpdate[];
causalChain: string[];
dependencies: string[];
strongestCountercase: string;
whyAboveNext: string;
whyBelowPrevious: string;
scenarioTests: ScenarioTest[];
signalIds: string[];
entrepreneurOpening: string;
};
claimIds: string[];
}
The conditional rank probability mass function must sum to 1. Probabilities
are stored at full precision, displayed as whole percentages, and constrained
to the edition's 0.01–0.99 bounds. To prevent zero cells while preserving joint
rank capacity, published marginals mix 80% of the seeded simulation frequency
with 20% of a fixed capacity prior. For the 20 candidates, that prior is each
rank band's capacity divided by 20: {0.05, 0.10, 0.10, 0.25, 0.50}. Thus
every conditional cell is at least 0.01 and every band still sums across
candidates to its exact capacity {1, 2, 2, 5, 10}. Raw exact-rank and band
counts remain published.
Joint coherence within a category
An app's rank PMF may not be elicited or edited independently. For each category and storefront, all candidate archetypes are ranked together in a single reproducible joint model. Each simulation draw assigns at most one candidate to each exact rank from 1 through 10. The published band PMFs are marginals aggregated from those exact-rank draws.
The joint model includes the ten displayed archetypes and enough explicitly
labelled other-field candidates to represent incumbent, unforeseen, or
unmodelled apps. Those placeholders are not additional forecast listings and
cannot be scored as successful archetype predictions. They prevent the model
from assuming that the project's ten selections must occupy all ten realised
positions.
Every joint-model artifact records:
- the category and storefront;
- the complete candidate IDs, including
other-fieldplaceholders; - model version and input-data hash;
- a deterministic random seed and at least 10,000 draws;
- the exact-rank marginal matrix for ranks 1 through 10 plus outside the Top 10; and
- a hash of the generated result.
For every draw, ranks are mutually exclusive. Across the resulting marginals,
the sum of probability assigned to any exact rank is exactly 1, including
other-field candidates. Consequently, the summed marginal capacity is
exactly 1 for rank 1, 2 for ranks 2–3, 2 for ranks 4–5, and 5 for ranks 6–10.
Because all twenty candidates are sorted, outside-top-10 has capacity 10.
The site's ten published app PMFs are projections from this joint artifact and
therefore may sum to less than those ranked capacities, but never more.
Manual narrative adjustments are inputs to a new joint-model run; they may not overwrite a single app's marginal after simulation. Category-existence probability is common to every candidate in the category/storefront and is applied only after the conditional joint ranking has been generated.
Numerical input and algorithm annex
data/SCHEMA.md and scripts/build-forecast-model.mjs are normative,
versioned parts of method joint-rank-v3; a frozen edition records their
SHA-256 hashes. The complete authoring JSON, numerical inputs, generated joint
artifacts and result hashes are public. The current algorithm is:
storefrontFit = clamp(lensFit + storefrontAdjustment, 0.01, 0.99)
combinedStrength = 0.60 × baseStrength + 0.40 × storefrontFit
location = logit(0.02 + 0.96 × combinedStrength)
spread = 0.15 + 1.25 × uncertainty
utility = location + spread × seededNormalDraw
For each category/storefront, every draw ranks the ten forecast archetypes
together with ten published other-field candidates whose base strengths are
0.92, 0.84, 0.76, 0.68, 0.60, 0.52, 0.44, 0.36, 0.28, 0.20, uncertainty is
0.65, and lens fit equals base strength. Default runs use 20,000 draws and
may never use fewer than 10,000. The random seed is the first little-endian
32-bit integer of SHA-256 over
joint-rank-v3|editionId|categoryId|storefrontId; the generator is Mulberry32
and normal draws use the Box–Muller transform. Stable candidate ID breaks an
exact utility tie. The script and artifact expose position totals so this can
be independently reproduced rather than trusted as a prose claim.
The authored app inputs are explicit forecast judgements, not observed measurements. They use these anchors:
| Input | 0.2 | 0.5 | 0.8 |
|---|---|---|---|
baseStrength | Need is plausible but adoption/reference class is weak | Repeated adjacent use exists, but distribution or habit is unsettled | Delivered recurring behaviour, strong need and a credible mass distribution route |
lensFit | Material local access, policy, infrastructure or affordability barriers | Mixed or neutral local conditions | Local infrastructure, need and distribution reinforce adoption |
storefrontAdjustment | Down to -0.15: strong local mismatch relative to peers in the lens | 0: no defensible local movement from the coarse prior | Up to +0.15: strong delivered local advantage relative to peers in the lens |
uncertainty | Delivered system with several independent observed sources | Important dependencies or scenario sensitivity remain | Policy targets, pilots or weak signals dominate |
Intermediate values interpolate between the anchors. Every app publishes its
reference class, positive and negative evidence IDs, dependencies, regional
fit values, a base-strength rationale, an uncertainty rationale, twelve
separate lens-fit rationales, and strongest counter-case beside the inputs.
For every storefront in a multi-country lens it also publishes a local
evidence record, a zero-sum ten-app adjustment vector, and an app-specific
reason for each non-gap value.
Each rationale compares the app's actual need, reference class, distribution,
evidence, dependencies or local conditions with the nearest anchors. Inputs
must never be generated from file order, app ID, current/displayed rank, a
fixed decrement, keyword matching or a category-wide constant. Every category
existence probability uses the same calibrated verbal ladder: 0.1 remote,
0.3 unlikely, 0.5 balanced, 0.7 more likely than not, and 0.9
highly likely because a durable discovery surface or delivered successor is
evident. Values between anchors require a short comparison with the nearest
two anchors in the public category research.
Every one of the 621 category/storefront probabilities has an
existenceCalibrationByStorefront record. Validation requires the probability
to match the scored input, the lens and storefront to match the frozen basket,
the exact nearest anchors to match the value, every evidence ID to resolve and
appear in the rationale, and upward and downward movement conditions to be
present. If a blind packet has no local evidence for a cell, the source list
must remain empty and the rationale must expose that gap rather than borrowing
another storefront's evidence. A fresh calibrator authors each storefront judgement from a sealed
packet that excludes all previous category probabilities, global scores,
inventory ranks and joint-model output. The 621 rationales must be unique and
must compare the evidence with both nearest anchors. Only after authoring is
complete are probabilities integrated and the simulation rerun. The complete
reader-facing table is rendered from those authored records in
research/category-calibrations.md; it is not the source of them.
Category existence is estimated separately for all 27 storefronts, including
Brazil and Mexico and each of South Africa, Nigeria and Kenya. An app begins
with one of twelve broad lensFit priors. For the 20 storefronts inside the
five multi-country lenses, a fresh author then judges an explicit storefront
adjustment for every one of the ten apps before seeing generated ranks. Each
storefront's vector is bounded to -0.15–+0.15 on a 0.025 grid and sums to
zero, so it changes relative conditional ordering without silently inflating
the whole lens. The model consumes the clamped sum as storefrontFit.
There are 460 category/storefront adjustment records and 6,210 numeric cells:
1,620 independently reasoned multi-country app adjustments, 2,980 forced-zero
gap cells, plus 1,610 single-storefront zero adjustments implicit in their
already country-specific lens priors. Where the sealed local packet contains no
evidence, all ten adjustments must remain zero, sources must remain empty and
the gap must be stated. Only a source whose registered geography names the
exact storefront country qualifies as local evidence; regional, global and
neighbouring-country sources remain context and may not justify nonzero app
adjustments. The complete public record is rendered in
research/app-storefront-adjustments.md.
Lens-fit evidence is a three-level chain: each app publishes its direct
reference class and counterevidence; its category publishes driver IDs used for
regional infrastructure, policy, need and access; and each multi-country
storefront adjustment cites the local category-calibration evidence from its
rank-blind packet and names why the app's capabilities or delivery route move.
A separately authored country may legitimately retain the same value as a
peer when the evidence supports no finer distinction; the method does not
mechanically perturb equal values merely to create geographic variation.
A category may name D01–D16
directly or a calibrated category-driver registry entry; every registry entry
in turn names one or more D01–D16 structural drivers. Validation requires every
link to resolve. Because
Brazil and Mexico are equal storefronts inside the Latin America lens, every
D01–D16 driver must include at least one registered Brazilian and one Mexican
record; evidence from one country, or from another region, cannot silently
stand in for the other. The same rule applies separately to South Africa,
Nigeria and Kenya. The local records remain inputs to judgement, not a formula
that mechanically creates lensFit, storefrontAdjustment or category-
existence probability.
This rubric does not pretend analyst judgement is mechanically deduced from sources. It makes that judgement inspectable and freezes it before simulation. A refresh records the old input, new input, numeric delta, evidence IDs and a short attribution to one or more anchors; it then reruns the whole joint model. Two analysts may disagree on an input, but neither can silently change a PMF, seed, field competitor or aggregation rule after seeing the result.
Rank-band points are:
| Rank band | Points |
|---|---|
| 1 | 10 |
| 2–3 | 8.5 |
| 4–5 | 6.5 |
| 6–10 | 3 |
| Outside Top 10 | 0 |
For storefront s:
jointTop10Probability
= P(category exists in r)
× P(rank is 1–10 | category exists in r)
expectedTop10Points
= P(category exists in r)
× Σ P(rank band b | category exists in r) × points(b)
For a lens with storefronts s, its categoryCoverageScore,
jointTop10CoverageScore, and expectedTop10Points are the arithmetic means
of those storefront values. Its conditional rank PMF is not a simple mean:
it weights every storefront PMF by that storefront's category-existence
probability and normalises by the sum of existence probabilities:
lensConditionalPmf(b)
= Σ_s P(category exists in s) × P(b | category exists in s)
/ Σ_s P(category exists in s)
This guarantees that lens coverage multiplied by the lens conditional Top-10 mass equals the mean storefront joint Top-10 probability. The summary remains a mixture for a uniformly sampled storefront, not a single lens event.
globalExpectedTop10Points and globalTop10CoverageScore are equal-lens means
of those within-lens means. The latter is expected storefront coverage under
this two-stage sampling design. It is not a probability of a nonexistent
global Apple-chart event and is not scored as one. Storefront joint Top-10
probabilities are scored separately, then macro-averaged by lens.
The ten apps are sorted by global expected Top 10 points; this mechanically sets the displayed rank. A precise rank therefore remains a visible commitment. The contract does not claim a probability distribution or confidence interval around that synthetic composite rank, because it has no joint cross-storefront dependence model.
Instead, typicalStorefrontRankBand answers a different, labelled question:
conditional on the category existing in a storefront selected by the
two-stage equal-lens/equal-storefront design, which smallest contiguous set of
rank bands contains at least 80% of the conditional mass? Its global
conditional PMF is computed by weighting each storefront PMF by that
storefront's category-existence probability, first giving each storefront an
equal share inside its lens and then each lens an equal share, before
normalising. If several intervals use the same minimum number of adjacent
bands, choose the one with greatest mass; if still tied, choose the interval
containing the better ranks. The UI must call this a typical-market rank
range, never an interval around the displayed consensus rank.
Every app explanation must state why it sits above the following app and below the preceding app. The first and tenth entries explain only the applicable side.
6. Evidence and public-process semantics
Every material claim points to a structured claim record, and every claim points to one or more source records.
interface Source {
id: string;
title: string;
publisher: string;
url: string;
archiveUrl?: string;
publicationDate?: string;
accessedDate: string;
cutoffCheck: {
evidenceCutoff: string;
accessedOn: string;
publicationTiming: "dated-or-current-at-access" | "undated-at-access";
latestKnownPublicationDate: string | null;
};
geography: string;
originalLanguage: string;
linkedLanguage: string;
translationMethod: string;
sourceType:
| "official-policy"
| "official-statistics"
| "company-filing"
| "research-paper"
| "industry-data"
| "expert-analysis"
| "journalism";
status: "used" | "contradictory" | "rejected" | "superseded";
claimIds: string[];
limitations: string[];
metadataStatus: "complete" | "qualified";
metadataCaveats: string[];
rejectionReason?: string;
snapshotHash?: string;
}
interface Claim {
id: string;
text: string;
kind:
| "observed-fact"
| "reported-projection"
| "policy-intent"
| "inference"
| "assumption";
sourceIds: string[];
geographicScope: string[];
supportsEntityIds: string[];
contradictsEntityIds: string[];
analystNote: string;
}
interface ResearchEvent {
id: string;
occurredAt: string;
actorType: "human" | "ai" | "tool";
actorLabel: string;
question: string;
inputIds: string[];
outputIds: string[];
publicSummary: string;
promptId?: string;
modelId?: string;
decisionId?: string;
}
The source-registry build extracts a machine-readable access date and the
latest explicit full or month-level publication date from each record. It
fails if either falls after the edition cutoff. Validation then compares the
stored cutoffCheck with both the manifest cutoff and the calendar date of its
exact asOf timestamp. Undated live pages remain explicitly labelled
undated-at-access; a missing publication date is never silently converted
into precision. The parser covers ISO and slash-separated numeric forms,
day/month-name and month/day-name forms, abbreviated months, month/year and
standalone future years. An ambiguous slash date is evaluated under both
common day-first and month-first readings; if either valid reading crosses the
cutoff, the later reading governs and the record fails closed.
claimIds is a generated reverse index, not an analyst's memory. It names the
driver, category-claim and app entity IDs that cite the source in the edition's
authored inputs. Refresh source deltas must reproduce this exact set alongside
the source's normalized publication date, access date and geography. Edition
validation independently rebuilds the reverse index from the edition-local
driver, category/calibration and app files and rejects a fabricated snapshot
entry before refresh deltas are considered.
Source metadata is fail-closed but not fabricated. Required public fields may
contain an explicit qualified value such as “publisher not separately
recorded” or “original language not recorded at source-record level”; in that
case metadataStatus must be qualified, metadataCaveats must identify the
inference or omission, and the record must carry a conservative limitation.
Only a record whose fields came directly from its structured source block may
be complete. The edition validator rejects null required fields, empty
limitations and qualified records without caveats.
Publication rules:
- Every material factual claim has an inline citation.
- Every material source opened and reviewed is registered, including rejected, contradictory, and superseded sources.
- The site publishes original analysis, structured extracts, research questions, relevant prompts, decisions, and links to originals.
- It does not claim to publish private model chain-of-thought. Public summaries give evidence, assumptions, alternatives, decisions, and critic verdicts.
- Copyrighted sources are summarised and linked, not reproduced wholesale.
- “All research” means the complete reviewed research register, not every search-engine result that happened to be returned.
- Source removal, forecast revision, and factual correction are separately dated events. None may silently rewrite history.
7. Forecast-production sequence
- Horizon scan: collect trends, emerging issues, weak signals, and plausible discontinuities.
- Driver map: classify political, economic, societal, technological, legislative, environmental, geopolitical, infrastructure, energy, capital, demographic, and platform-power drivers. Record impact, uncertainty, direction, geography, and evidence strength.
- Three Horizons: distinguish the established system, transition pressures, and emerging replacements.
- Critical uncertainties: identify high-impact, high-uncertainty drivers.
- Four scenarios: choose two sufficiently independent uncertainty axes and construct a 2×2 set. Do not assign scenario probabilities.
- Wind-tunnel: test every category and app causal chain against all four
scenarios and relevant regional lenses. Read the full archetype, provider,
delivery, causal chain, dependencies, regional variance and counter-case for
each judgement. Never derive scenario viability or rank band from the app's
current rank/index, keyword matching, regular expressions or a fixed
template. The retained
author-scenario-tests.mjsfilename is a validator; it does not author or overwrite these qualitative records. - Forecast synthesis: begin with a named reference class or base rate, record positive and negative evidence updates, and then state probabilities.
- Signals: define observations that would raise or lower each forecast.
- Ranking: calculate regional and global order from the frozen probability distributions. Do not hand-edit positions.
interface ScenarioTest {
scenarioId: string;
viability: "strong" | "survives" | "weak" | "fails";
likelyRankBand: RankBand;
rationale: string;
brokenAssumption: string;
}
Scenario labels are explanatory, not ranking inputs: viable in all four is
robust; three is resilient; one or two is scenario-dependent; failing in
three or four is fragile.
8. Scoring contract
Each edition is scored independently against the same eventual outcome. Later editions must never be presented as if they were the original five-year forecast.
- Binary category-existence and joint app-Top-10 claims use logarithmic loss and Brier score.
- Ordinal rank-band probability distributions use multiclass logarithmic loss, with Ranked Probability Score as a distance-sensitive diagnostic.
- Ranked Top 10 lists use Rank-Biased Overlap at depth 10 with persistence
parameter
p = 0.9. - Top 10 overlap, Kendall's tau-b on matched archetypes, mean absolute rank error, unforeseen entries, and category precision/recall are secondary diagnostics.
- Calibration tables and reliability plots carry explicit sample-size warnings.
For binary outcome y ∈ {0,1} and probability p:
logLoss = -(y ln(p) + (1-y) ln(1-p))
brier = (p-y)^2
Lower is better for both. Probability bounds prevent infinite logarithmic
loss. The full retrospective protocol and win condition against the naïve
baseline are defined in BACKTEST.md.
9. Signal and update contract
interface Signal {
id: string;
statement: string;
directionIfObserved: "raises" | "lowers" | "ambiguous";
threshold: string;
geography: string[];
cadence: "monthly" | "quarterly" | "annual" | "event-driven";
sourceIds: string[];
nextCheckAt: string;
}
A refresh must:
- Keep
asOfnull while the edition is a mutable research draft; seal preparation assigns the exact close timestamp and locks the status. - Search for evidence published after the previous cutoff while retaining the previous source register.
- Recheck every due signal.
- Record old probability, new probability, delta, evidence IDs, and reason.
- Recompute ranks mechanically.
- Publish additions, removals, movements, and unchanged forecasts.
- Create a new immutable edition rather than editing the old one.
Freeze and publication seal
researching editions are mutable working drafts, keep asOf: null, and must
be labelled as such. Seal preparation atomically assigns the exact current
asOf time and moves the manifest to closing before validation. Every
authoring, packet, model and evidence-snapshot writer accepts only
researching, so the forecast cannot change after its claimed close. A failed
preparation restores researching and asOf: null; a successful one remains
locked in closing. Sealing is deliberately two-stage so an external receipt
can never be requested for a root that does not exist yet.
prepare:sealvalidates the edition, computes a canonical sorted path/hash bundle over authored inputs, the edition-localsources.jsonanddrivers.jsonsnapshots, normative method/schema/ seal scripts, inventory and every joint-model artifact, then writes an exact seal proposal containing that root. The manifest is represented in the root by a canonical projection of every field except the lifecycle-onlystatus,frozenAt,freezeRootandpublishedAtfields. This permanently binds the question,asOf, market, chart, probability, scoring and model parameters while still allowing finalisation to change lifecycle state. Shared rootdata/sources.jsonanddata/drivers.jsonare working inputs for the next edition and are deliberately excluded: each edition manifest binds the hashes of its own copies, which the edition seal includes.- The owner publishes that exact proposal with the complete sealed content at
a
raw.githubusercontent.comURL whose Git object path contains a full immutable 40–64 hexadecimal revision ID. Seal protocol v1 then requires an Internet Archive Waybackid_snapshot of that exact immutable URL. The fourteen-digit archive timestamp is the independent receipt ID. freeze:editionaccepts only thatclosingedition, recomputes the root, byte-compares the remote proposal, verifies that the immutable URL uses the supplied full revision, fetches the archived copy and byte-compares it too, and only then appends the root, files, response hashes and anchor identifiers todata/freeze-ledger.jsonand changes the manifest tofrozen.- Every later validation repeats both remote fetches and content checks in addition to recomputing all local hashes. Unavailable or changed external evidence is a validation failure, not an invitation to trust the ledger.
The builder refuses an edition when either its manifest is not researching
or its ID already appears in the ledger, so merely flipping the manifest
back cannot authorise a rebuild. Re-preparing after any content change requires
deleting the unanchored proposal first; an anchored or ledgered edition can
never be prepared again.
A local proposal or ledger is not an independent timestamp. Therefore an edition may not be labelled published until the immutable Git object and its byte-identical Wayback snapshot exist. Supporting another timestamp authority requires a new, reviewed seal-protocol version; an arbitrary receipt URL or self-declared authority is never accepted. The current build scope does not authorise either external action, so it remains a research draft. A refresh skill may only copy from a frozen, externally attested source edition.
A change is material when a category is added, merged, split, renamed, or retired; a probability changes by at least five percentage points; a displayed rank moves by at least two places; a typical-market range crosses the Top 10 boundary; a core dependency or counter-case changes; or new evidence overturns a material assumption.
10. July 2031 resolution contract
For every included storefront and category:
- Capture the official Top Free iPhone chart at local noon every day from 1–31 July 2031.
- Preserve the raw response, screenshot, retrieval time, URL, and hash.
- Give daily rank
kthe value11-k; give nonappearance zero. - Require at least 16 valid daily captures.
- Average daily points and sort them to form the realised monthly Top 10.
- Apply the locked archetype definitions through blind independent mapping.
- Resolve a storefront category as existing only if it meets the 16-day category rule.
The canonical source is the signed-out, human-facing first-party iPhone
category page that visibly identifies the storefront, category and Top Free
chart. RSS or other structured feeds are diagnostics only; they never replace
the canonical ranks merely because they differ. By
2031-06-30T23:59:59Z, before any outcome day, publish and hash a resolution
source registry. For categories already live, it contains the exact canonical
URL, category identifier, rendered-chart selector or extraction rule, locale,
and one successful test capture for every storefront/category pair.
Proposed-new, renamed, merged or split categories cannot be required to have a URL before they launch. For those forecast categories, the June registry instead freezes: (a) the exact signed-out top-level category-directory URL and selector; (b) a daily local-noon procedure that records every category link, label, identifier and first-seen timestamp before opening any linked chart; (c) the forecast category's already-published inclusion and exclusion boundary; and (d) the following mapping rule, which is fixed by this contract now and may only be copied—not redesigned—in June 2031.
Before opening any discovered chart, three coders work independently. They may
see only the directory link label and identifier plus the destination page's
title, breadcrumb and category description. They may not see chart positions,
app names, icons, ratings, reviews or another coder's answer. Each coder must
assign the discovered link to exactly one published forecast-category ID or to
no-match, using the already-frozen inclusion and exclusion rules. A link maps
only when at least two of the three coders independently choose the same
forecast-category ID. A split vote is no-match; it is not adjudicated after
chart data become visible.
Assignments are one-to-one in both directions: one observed chart may resolve
at most one forecast category, and one forecast category may use at most one
observed chart. If two qualifying links receive the same forecast-category
assignment, the link with the earlier directory first-seen timestamp wins. A
same-timestamp tie is resolved by lexicographically ascending Apple category
identifier, then by canonical URL. The other link remains preserved as
discovery evidence but cannot contribute ranks or existence to another
forecast category. The predecessor/successor arrays in categories.json
document the taxonomy hypothesis only; they do not pre-assign a 2031 chart or
override this rule.
A newly discovered link accepted under that rule becomes the category's canonical page from its first observed day. Its chart is captured that same day and on every remaining July day. A launch on 16 July can therefore still supply the 16 valid days needed to qualify; a launch after 16 July cannot satisfy the existence rule. Surprise categories are preserved as discovery evidence but cannot be retrofitted to a forecast category whose frozen boundary they do not meet. The 30 June registry must include the exact candidate-label sheet, coder instructions, assignment form and a digest of this unchanged rule before any July discovery record is collected.
Current-category fate resolution
The retained, renamed, merged, split and retired fields are testable
categorical forecasts, not decorative lineage notes. They resolve separately
for every storefront using current-category-fate-v1 in categories.json.
The same three rank-blind coders receive each frozen 2026 predecessor name,
identifier and boundary plus only a discovered 2031 link's label, identifier,
title, breadcrumb and category description. They may not see ranks, apps,
icons, ratings, reviews or one another's answers. Two of three votes establish
whether an observed chart continues a predecessor purpose or, for a split,
covers a material subdomain. This continuity coding is separate from the
one-to-one forecast-category assignment above: an observed merged chart may
continue several predecessors without being assigned to several forecast
categories.
For a storefront, the fates resolve as follows:
- Retained: the single listed successor qualifies; that same observed
chart receives continuity votes for the predecessor; its normalized
directory label equals the normalized 2026
currentName; and no second observed chart independently continues the predecessor. - Renamed: the single listed successor qualifies and receives predecessor
continuity votes; its normalized directory label differs from the 2026
currentName; and no separate chart with the old normalized label independently continues the predecessor. - Merged: exactly one chart qualifies for the shared listed successor; every 2026 predecessor that names that successor receives continuity votes for that same chart; and none has a separate independent continuation. The whole inferred merge group must pass—a partial group is a failure.
- Split: the predecessor has no independent continuation; every listed successor qualifies as a distinct observed chart; and every successor receives continuity votes that it covers a material subdomain of the frozen predecessor boundary. Requiring all listed successors is deliberate: two or a majority is insufficient.
- Retired: the predecessor has no independent continuation and no qualifying listed successor.
Label normalization is Unicode NFKC, lower-case, trimmed, whitespace-collapsed text with punctuation removed. If an old-label or otherwise independent predecessor chart coexists with a renamed, merged or split successor, the transformed fate fails even when the successor exists. A fate is ambiguous only when technical unavailability of the frozen directory or qualifying page prevents a determination on more than fifteen July days; otherwise unmet conditions are failures. Report per-storefront outcomes, equal-storefront lens success shares and an equal-lens descriptive global share. These fates have no probability forecast in this edition and therefore do not enter the proper probability score.
If a registered canonical request fails at local noon, retry the same URL at +5, +15, and +30 minutes. If all attempts fail, that day is missing. Do not switch surfaces during July. A fallback may be used only when it was named and test- captured in the frozen June registry and the canonical surface was already documented as permanently withdrawn before the registry froze. A web/feed disagreement is preserved as provenance but resolved in favour of the frozen human-facing page. For a category already live by 30 June, failure to register a qualifying page makes the storefront/category unresolvable. For a proposed or transformed category, no page before launch is expected: resolvability comes from the frozen directory-discovery procedure and its daily captures. Fewer than 16 valid chart days after a qualifying first appearance means the category does not meet the 16-day existence rule; it is ambiguous only when technical unavailability of the frozen directory or qualifying chart prevents that determination. No surface or category mapping may be chosen after chart outcomes are inspected.
For a multi-storefront lens, report the share of valid storefronts in which the category exists, and the equal-storefront mixture of realised rank bands and points. Do not collapse those distinct outcomes to one invented lens rank. Global category coverage is reported descriptively as the equal-lens mean of those valid-storefront shares. It is not compared with a purported global existence probability; proper scoring applies to each storefront forecast and is macro-averaged first within lenses and then globally. If a category does not exist, its apps' joint Top 10 outcomes resolve No; conditional rank forecasts are not separately scored. If the first-party App Store ceases and no successor defined in the frozen edition satisfies the contract, the affected questions are annulled rather than retrospectively redefined.
The scorecard must show the original 2026 edition, every refresh, the naïve persistence baseline, ambiguous outcomes, and missing data separately.
11. Machine-checkable invariants
Validation must reject an edition when:
- A primary-storefront category has other than ten ranked apps.
- Displayed ranks are missing, duplicated, or disagree with computed order.
- A rank probability mass function does not sum to 1 within numeric tolerance.
- A regional/category rank artifact was not generated from one reproducible joint model, assigns two candidates to one exact rank in a draw, or its exact-rank and band-capacity sums fail the joint-coherence rules.
- A published app PMF differs from the marginals in its referenced joint-model artifact.
- A probability lies outside the frozen bounds.
- A material claim has no source, or a source has no URL and limitations note.
- A fictional element lacks its explicit disclosure flag.
- A current category is absent from both the primary taxonomy and the retired/merged account.
- A new category has fewer than ten distinct archetypes.
- An app lacks a counter-case, regional forecasts, scenario tests, signals, or an adjacent-rank explanation.
- A later edition mutates a previously published snapshot.
AppStore2031