@viccydev/pi-fpa 0.8.1 → 0.9.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -3
- package/extensions/fpa-artifacts/compose.ts +186 -47
- package/extensions/fpa-artifacts/contracts.ts +202 -2
- package/extensions/fpa-artifacts/finalize.ts +99 -0
- package/extensions/fpa-artifacts/forecast-quality.ts +138 -0
- package/extensions/fpa-artifacts/index.ts +36 -1
- package/extensions/fpa-dashboard/coordinator.ts +2 -2
- package/extensions/fpa-dashboard/cycle-operating-projection.ts +5 -4
- package/extensions/fpa-dashboard/forward-outlook.ts +5 -2
- package/extensions/fpa-dashboard/index.ts +2 -2
- package/extensions/fpa-dashboard/projector.ts +58 -1
- package/extensions/fpa-dashboard/service.ts +13 -2
- package/extensions/fpa-dashboard/stage-projector.ts +60 -2
- package/extensions/fpa-routing-guard/index.ts +8 -3
- package/graphs/fpa-forecast-freeze.json +35 -39
- package/package.json +3 -3
- package/skills/fpa-apply-core-rules/SKILL.md +4 -2
- package/skills/fpa-apply-core-rules/references/core-rules.md +5 -2
- package/skills/fpa-diagnose-actuals/references/artifact-contract.md +2 -0
- package/skills/fpa-forecast-approved-strategy/SKILL.md +23 -17
- package/skills/fpa-forecast-approved-strategy/references/artifact-contract.md +34 -6
- package/skills/fpa-refresh-dashboard/SKILL.md +15 -10
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
|
-
"description": "FP&A 正式预测冻结:消费已审核策略交接与主 Agent 已提交的精确 strategy_decision
|
|
3
|
-
"maxSteps":
|
|
2
|
+
"description": "FP&A 正式预测冻结:消费已审核策略交接与主 Agent 已提交的精确 strategy_decision,生成预测决策计划并通过确定性 finalize module 冻结 approved_cycle_forecast;Graph 不请求审批、不发布仪表盘。",
|
|
3
|
+
"maxSteps": 6,
|
|
4
4
|
"mutationPolicy": "mutating",
|
|
5
5
|
"name": "fpa-forecast-freeze",
|
|
6
6
|
"nodes": [
|
|
@@ -11,29 +11,29 @@
|
|
|
11
11
|
"next": "route_confirmed_strategy",
|
|
12
12
|
"outputKey": "confirmed_strategy",
|
|
13
13
|
"parseJson": true,
|
|
14
|
-
"prompt": "操作备注:{{goal}}\n\n读取 artifacts/reviewed_strategy_handoff.json、其中绑定的 strategy_proposal 与 strategy_review,以及操作备注明确给出的 strategy_decision path。必须核验 handoff status=ready;scope_id/cycle_id
|
|
14
|
+
"prompt": "操作备注:{{goal}}\n\n读取 artifacts/reviewed_strategy_handoff.json、其中绑定的 strategy_proposal 与 strategy_review,以及操作备注明确给出的 strategy_decision path。必须核验 handoff status=ready;scope_id/cycle_id 与 Graph context 一致;proposal、review、handoff 的 strategy_version 一致;decision.kind=fpa.strategy.decision、decision=confirm;decision 的 strategy_version、handoff_fingerprint、decision_fingerprint 与操作备注和当前 handoff 精确一致。handoff 中的旧业务 forecast_role 仅作为兼容元数据,不参与 ledger role 判断;最终角色由冻结 module 按目标周期确定。任一身份或审批不一致即 blocked。不得请求或记录第二次审批,不得调用任何 fpa_dashboard_* 工具。\n\n只输出 JSON:{\"status\":\"ready|blocked\",\"strategy_version\":\"...\",\"strategy_decision_path\":\"...\",\"strategy_decision_fingerprint\":\"...\",\"review_conditions\":[],\"blockers\":[]}",
|
|
15
15
|
"skills": ["fpa-apply-core-rules"],
|
|
16
|
-
"systemPrompt": "你是 FP&A 已确认策略核验 Agent。只读核验主 Agent 已提交的不可变决策,不批准、不预测、不发布仪表盘。权威上下文为 scope_id={{context.scope_id}}、cycle_id={{context.cycle_id}}
|
|
16
|
+
"systemPrompt": "你是 FP&A 已确认策略核验 Agent。只读核验主 Agent 已提交的不可变决策,不批准、不预测、不发布仪表盘。权威上下文为 scope_id={{context.scope_id}}、cycle_id={{context.cycle_id}}。",
|
|
17
17
|
"tools": ["read"],
|
|
18
18
|
"type": "subagent"
|
|
19
19
|
},
|
|
20
20
|
{
|
|
21
21
|
"cases": [
|
|
22
22
|
{ "equals": "ready", "label": "确认有效", "to": "draft_forecast" },
|
|
23
|
-
{ "equals": "blocked", "label": "确认无效", "to": "
|
|
23
|
+
{ "equals": "blocked", "label": "确认无效", "to": "report_strategy_blocked" }
|
|
24
24
|
],
|
|
25
|
-
"default": { "label": "其他结果", "to": "
|
|
25
|
+
"default": { "label": "其他结果", "to": "report_strategy_blocked" },
|
|
26
26
|
"id": "route_confirmed_strategy",
|
|
27
27
|
"label": "判断是否可预测",
|
|
28
28
|
"path": "data.confirmed_strategy.status",
|
|
29
29
|
"type": "router"
|
|
30
30
|
},
|
|
31
31
|
{
|
|
32
|
-
"id": "
|
|
33
|
-
"label": "
|
|
32
|
+
"id": "report_strategy_blocked",
|
|
33
|
+
"label": "报告策略核验阻断",
|
|
34
34
|
"outputKey": "forecast_blocked",
|
|
35
35
|
"parseJson": true,
|
|
36
|
-
"prompt": "
|
|
36
|
+
"prompt": "策略核验结果:{{data.confirmed_strategy}}\n\n汇总实际阻断原因。只输出 JSON:{\"kind\":\"fpa.graph-handoff\",\"status\":\"blocked\",\"graph\":\"fpa-forecast-freeze\",\"blockers\":[],\"required_action\":\"需要主会话处理的治理或策略动作\"}",
|
|
37
37
|
"systemPrompt": "用中文简洁报告预测阻断,只输出约定 JSON。",
|
|
38
38
|
"type": "prompt"
|
|
39
39
|
},
|
|
@@ -41,55 +41,51 @@
|
|
|
41
41
|
"agentName": "draft_forecast",
|
|
42
42
|
"id": "draft_forecast",
|
|
43
43
|
"label": "正式预测决策计划",
|
|
44
|
-
"next": "
|
|
44
|
+
"next": "route_draft_forecast",
|
|
45
45
|
"outputKey": "draft_forecast",
|
|
46
|
-
"
|
|
46
|
+
"parseJson": true,
|
|
47
|
+
"prompt": "操作备注:{{goal}}\n\n已核验策略与决策:{{data.confirmed_strategy}}\n\n读取 reviewed_strategy_handoff、strategy_proposal、strategy_review 和精确 strategy_decision,按 fpa-forecast-approved-strategy 写 artifacts/forecast_plan.json。不得重新优化策略;业务分配、范围、假设或审核条件需变化时不得写入计划,必须返回 blocked 并回到策略规划。只写 allocation 与每个切片的 ROAS assumptions;revenue、汇总、单位、frozen_at、forecast_role、数据覆盖状态和修复提醒全部由下游 fpa_forecast_finalize 确定。优先使用 ua_spend 的 app_code/platform/media_source 真实组合;若已确认分配中的部分切片与当前上游映射不一致,不得擅自删除、置零、重分配或伪造映射,保留原切片交给 finalize 自动产出 complete_with_limits、覆盖率和数据修复项。不得调用任何 fpa_dashboard_* 工具。成功写入后只输出 JSON:{\"status\":\"ready\",\"plan_path\":\"artifacts/forecast_plan.json\",\"blockers\":[]};需要治理处理时只输出 JSON:{\"status\":\"blocked\",\"plan_path\":null,\"blockers\":[]}。",
|
|
47
48
|
"skills": ["fpa-apply-core-rules", "fpa-forecast-approved-strategy"],
|
|
48
49
|
"systemPrompt": "你是 FP&A 正式预测 Agent。只按已确认策略写预测计划,不请求审批、不发布仪表盘、不手算派生值。",
|
|
49
50
|
"tools": ["read", "write", "fpa_calc"],
|
|
50
51
|
"type": "subagent"
|
|
51
52
|
},
|
|
52
53
|
{
|
|
53
|
-
"
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
"
|
|
58
|
-
"
|
|
59
|
-
"
|
|
60
|
-
"
|
|
54
|
+
"cases": [
|
|
55
|
+
{ "equals": "ready", "label": "计划已写入", "to": "finalize_forecast" },
|
|
56
|
+
{ "equals": "blocked", "label": "需要治理处理", "to": "report_plan_blocked" }
|
|
57
|
+
],
|
|
58
|
+
"default": { "label": "其他结果", "to": "report_plan_blocked" },
|
|
59
|
+
"id": "route_draft_forecast",
|
|
60
|
+
"label": "判断预测计划是否可冻结",
|
|
61
|
+
"path": "data.draft_forecast.status",
|
|
62
|
+
"type": "router"
|
|
61
63
|
},
|
|
62
64
|
{
|
|
63
|
-
"
|
|
64
|
-
"
|
|
65
|
-
"
|
|
66
|
-
"
|
|
67
|
-
"
|
|
68
|
-
"
|
|
69
|
-
"
|
|
70
|
-
"systemPrompt": "你是预测计划修复 Agent,只按确定性合成报错做最小修正。",
|
|
71
|
-
"tools": ["read", "write", "edit"],
|
|
72
|
-
"type": "subagent"
|
|
65
|
+
"id": "report_plan_blocked",
|
|
66
|
+
"label": "报告预测计划阻断",
|
|
67
|
+
"outputKey": "forecast_blocked",
|
|
68
|
+
"parseJson": true,
|
|
69
|
+
"prompt": "预测计划结果:{{data.draft_forecast}}\n\n汇总实际阻断原因。只输出 JSON:{\"kind\":\"fpa.graph-handoff\",\"status\":\"blocked\",\"graph\":\"fpa-forecast-freeze\",\"blockers\":[],\"required_action\":\"需要主会话处理的治理或策略动作\"}",
|
|
70
|
+
"systemPrompt": "用中文简洁报告预测阻断,只输出约定 JSON。",
|
|
71
|
+
"type": "prompt"
|
|
73
72
|
},
|
|
74
73
|
{
|
|
75
74
|
"args": {
|
|
76
|
-
"
|
|
77
|
-
"
|
|
78
|
-
|
|
79
|
-
"forecast_role": "{{context.forecast_role}}",
|
|
80
|
-
"scope_id": "{{context.scope_id}}"
|
|
81
|
-
}
|
|
75
|
+
"plan_path": "artifacts/forecast_plan.json",
|
|
76
|
+
"scope_id": "{{context.scope_id}}",
|
|
77
|
+
"cycle_id": "{{context.cycle_id}}"
|
|
82
78
|
},
|
|
83
|
-
"id": "
|
|
79
|
+
"id": "finalize_forecast",
|
|
84
80
|
"label": "冻结正式预测",
|
|
85
81
|
"mutates": true,
|
|
86
|
-
"outputKey": "
|
|
87
|
-
"tool": "
|
|
82
|
+
"outputKey": "finalize_forecast",
|
|
83
|
+
"tool": "fpa_forecast_finalize",
|
|
88
84
|
"type": "tool"
|
|
89
85
|
}
|
|
90
86
|
],
|
|
91
87
|
"schemaVersion": 1,
|
|
92
88
|
"start": "load_confirmed_strategy",
|
|
93
89
|
"transitionLabels": { "default": "其他情况", "error": "失败", "next": "继续" },
|
|
94
|
-
"version": "
|
|
90
|
+
"version": "3.0.0"
|
|
95
91
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@viccydev/pi-fpa",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Full-cycle FP&A planning, strategy, forecast, and review prompts, skills, and data tools for Pi",
|
|
6
6
|
"license": "UNLICENSED",
|
|
@@ -31,9 +31,9 @@
|
|
|
31
31
|
"fpa-dashboard-worker": "./bin/fpa-dashboard-worker.mjs"
|
|
32
32
|
},
|
|
33
33
|
"scripts": {
|
|
34
|
-
|
|
34
|
+
"test": "node tests/package-structure.test.mjs && node tests/workflow-routing.test.mjs && node --test tests/workflow-routing-guard.test.mjs tests/graph-contract.test.mjs tests/graph-installer.test.mjs && node tests/extension-unit.test.mjs && node --test tests/catalog-timeout.test.mjs tests/artifact-store.test.mjs tests/artifact-ledger.test.mjs tests/artifact-handoff.test.mjs tests/forecast-compose.test.mjs tests/forecast-finalize.test.mjs tests/dashboard-actuals.test.mjs tests/dashboard-projector.test.mjs tests/dashboard-module-publisher.test.mjs tests/dashboard-stage-projector.test.mjs tests/strategy-decision.test.mjs tests/cycle-operating-projection.test.mjs tests/forward-outlook.test.mjs tests/forecast-accuracy.test.mjs tests/dashboard-publisher.test.mjs tests/dashboard-provenance.test.mjs tests/dashboard-coordinator.test.mjs && node tests/pi-loader-smoke.mjs && node tests/worker-loader-smoke.mjs && node tests/publish-workflow.test.mjs",
|
|
35
35
|
"test:structure": "node tests/package-structure.test.mjs",
|
|
36
|
-
|
|
36
|
+
"test:unit": "node tests/extension-unit.test.mjs && node --test tests/catalog-timeout.test.mjs tests/artifact-store.test.mjs tests/artifact-ledger.test.mjs tests/artifact-handoff.test.mjs tests/forecast-compose.test.mjs tests/forecast-finalize.test.mjs tests/dashboard-actuals.test.mjs tests/dashboard-projector.test.mjs tests/dashboard-module-publisher.test.mjs tests/dashboard-stage-projector.test.mjs tests/strategy-decision.test.mjs tests/cycle-operating-projection.test.mjs tests/forward-outlook.test.mjs tests/forecast-accuracy.test.mjs tests/dashboard-publisher.test.mjs tests/dashboard-provenance.test.mjs tests/dashboard-coordinator.test.mjs",
|
|
37
37
|
"test:loader": "node tests/pi-loader-smoke.mjs && node tests/worker-loader-smoke.mjs",
|
|
38
38
|
"test:live": "node tests/live-smoke.mjs",
|
|
39
39
|
"pack:check": "npm pack --dry-run"
|
|
@@ -26,8 +26,10 @@ writing artifacts. It applies only when the current agent exposes both
|
|
|
26
26
|
| Exact Forecast and Execution refs plus newly arrived comparable Actuals | `fpa-cycle-review` |
|
|
27
27
|
|
|
28
28
|
3. Call `graph_run` with the user's complete goal. Every `context` value must
|
|
29
|
-
be a string
|
|
30
|
-
|
|
29
|
+
be a string. Provide exact `scope_id` and `cycle_id`; planning additionally
|
|
30
|
+
requires its business `forecast_role`, while forecast freeze derives the
|
|
31
|
+
ledger role from the target period and freeze time. Keep structured planning
|
|
32
|
+
details in `goal`. The parent agent must not
|
|
31
33
|
reproduce Graph nodes or pre-run their `fpa_*` calls. After the combined
|
|
32
34
|
planning Graph succeeds, the main Agent publishes `period-review` and then
|
|
33
35
|
`next-strategy` with their matching `fpa_dashboard_publish_*` tools. Graph
|
|
@@ -24,8 +24,11 @@ ready handoff plus the exact confirmed strategy-decision fingerprint:
|
|
|
24
24
|
|
|
25
25
|
`reviewed_strategy_handoff + strategy_decision -> forecast_plan -> approved_cycle_forecast`
|
|
26
26
|
|
|
27
|
-
The main Agent owns approval recording; the forecast Graph
|
|
28
|
-
|
|
27
|
+
The main Agent owns approval recording; the forecast Graph writes the approved
|
|
28
|
+
plan and delegates deterministic composition, lifecycle-role derivation, and
|
|
29
|
+
artifact freezing to `fpa_forecast_finalize`. A business role recorded in an
|
|
30
|
+
older handoff is lineage metadata, not a storage role that the model must
|
|
31
|
+
translate. A rejected approval reruns
|
|
29
32
|
`fpa-strategy-planning` with the exact feedback and produces a new version
|
|
30
33
|
rather than modifying the reviewed proposal in place.
|
|
31
34
|
|
|
@@ -36,34 +36,28 @@ from it, return `blocked`; never reconstruct or substitute the handoff.
|
|
|
36
36
|
4. Provide downside, base, and upside values for each supported KPI.
|
|
37
37
|
5. Reconcile allocation totals, formulas, and cross-metric identities.
|
|
38
38
|
6. Write the forecast plan to `artifacts/forecast_plan.json` using [artifact-contract.md](references/artifact-contract.md).
|
|
39
|
-
7.
|
|
40
|
-
`
|
|
41
|
-
`
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
period start and is the approved direct successor of the active cycle. Use
|
|
45
|
-
`backtest` for a historical rerun frozen after its target
|
|
46
|
-
period; it is immutable evidence, not an operating plan, and it must not
|
|
39
|
+
7. Call `fpa_forecast_finalize` with the plan path and the exact `scope_id` and
|
|
40
|
+
`cycle_id`. Do not choose or pass `forecast_role`: the tool derives
|
|
41
|
+
`next_plan` before period start, `eac` during the period, and `backtest`
|
|
42
|
+
after period end, then composes and commits the artifact atomically. A
|
|
43
|
+
historical rerun is immutable evidence, not an operating plan, and must not
|
|
47
44
|
replace the current pointer or enter the automatic operating dashboard
|
|
48
45
|
queue. It may be displayed from its exact immutable reference only when the
|
|
49
|
-
user explicitly requests a backtest dashboard publication.
|
|
50
|
-
`next_plan` is likewise stored as forward lineage and does not replace the
|
|
51
|
-
legacy current pointer; its explicit dashboard handoff links it to the
|
|
52
|
-
active cycle after successor validation.
|
|
46
|
+
user explicitly requests a backtest dashboard publication.
|
|
53
47
|
|
|
54
48
|
## Write the decision, not the arithmetic
|
|
55
49
|
|
|
56
50
|
The only judgement in a forecast is the allocation and each slice's ROAS
|
|
57
51
|
assumption. Revenue, the consolidated roll-up, units, windows, and `frozen_at`
|
|
58
|
-
all follow from those, and `
|
|
52
|
+
all follow from those, and `fpa_forecast_finalize` derives them — which is what
|
|
59
53
|
makes the identities the commit checks true by construction rather than
|
|
60
54
|
dependent on transcribing several hundred numbers without a slip.
|
|
61
55
|
|
|
62
56
|
So do not hand-compute revenue, totals, or ratios, and do not emit the full
|
|
63
57
|
artifact as one tool argument: that is the shape that gets cut off at the model
|
|
64
|
-
output limit and leaves a turn that reads finished but committed nothing.
|
|
65
|
-
|
|
66
|
-
|
|
58
|
+
output limit and leaves a turn that reads finished but committed nothing. Stop
|
|
59
|
+
after writing the plan; the finalizer owns composition and commit. Never claim
|
|
60
|
+
a fingerprint you were not handed.
|
|
67
61
|
|
|
68
62
|
## Status and the publish gate
|
|
69
63
|
|
|
@@ -84,16 +78,28 @@ tool. `complete_with_limits` remains publishable when its limitations are
|
|
|
84
78
|
explicit, while `blocked` is not. Do not hide a real defect to get through the
|
|
85
79
|
gate.
|
|
86
80
|
|
|
81
|
+
Upstream identity or coverage defects are non-blocking data limits. Preserve
|
|
82
|
+
the full approved allocation, leave affected revenue and ROAS values `NULL`,
|
|
83
|
+
and let `fpa_forecast_finalize` emit `data_quality`, a coverage-labelled partial
|
|
84
|
+
`conclusion`, and actionable repair items. Never delete a bad slice, turn it
|
|
85
|
+
into zero spend, or renormalize the remaining allocation. The affected slice
|
|
86
|
+
remains ineligible for Actuals comparison and execution until its mapping is
|
|
87
|
+
fixed. Approval mismatch, duplicate or negative allocation, budget mismatch,
|
|
88
|
+
invalid period/currency/role, and unauthorized execution remain blocking
|
|
89
|
+
governance failures.
|
|
90
|
+
|
|
87
91
|
## Boundaries
|
|
88
92
|
|
|
89
93
|
- Do not optimize, revise, or substitute the approved strategy while forecasting.
|
|
90
94
|
- A required change invalidates the current approval and routes back to recommendation and review.
|
|
91
95
|
- Unsupported KPIs remain `NULL` with a reason.
|
|
96
|
+
- A partial conclusion must say it covers only calculable slices and must show
|
|
97
|
+
`calculable_spend_pct`; it must never be labelled portfolio ROAS.
|
|
92
98
|
- Do not execute the strategy and do not start continuous monitoring.
|
|
93
99
|
|
|
94
100
|
## Completion
|
|
95
101
|
|
|
96
|
-
The forecast-freeze workflow ends only after `
|
|
102
|
+
The forecast-freeze workflow ends only after `fpa_forecast_finalize` returns
|
|
97
103
|
`status: committed`, an `immutable_fingerprint`, and (for assigned artifacts) a
|
|
98
104
|
`dashboard_handoff`. Commit freezes the business artifact but does not enqueue
|
|
99
105
|
or publish dashboard work. The calling main Agent owns that explicit handoff.
|
|
@@ -41,6 +41,28 @@ calibration_policy:
|
|
|
41
41
|
policy_version: string
|
|
42
42
|
unsupported_metrics: []
|
|
43
43
|
reconciliation_checks: []
|
|
44
|
+
data_quality:
|
|
45
|
+
status: complete | partial | unavailable
|
|
46
|
+
planned_spend: number
|
|
47
|
+
calculable_spend: number
|
|
48
|
+
calculable_spend_pct: number # 0..1; never renormalized
|
|
49
|
+
excluded_spend: number
|
|
50
|
+
issue_count: number
|
|
51
|
+
issues:
|
|
52
|
+
- issue_id: string
|
|
53
|
+
code: slice_key_not_in_actuals
|
|
54
|
+
severity: warning
|
|
55
|
+
slice: {app_id: string, store: string, channel_group: string}
|
|
56
|
+
affected_metrics: [actuals_comparison, revenue, roas, execution]
|
|
57
|
+
disposition: excluded_from_calculation
|
|
58
|
+
evidence: []
|
|
59
|
+
remediation: string
|
|
60
|
+
owner_role: data_steward
|
|
61
|
+
conclusion:
|
|
62
|
+
status: available | available_with_limits | unavailable
|
|
63
|
+
scope: portfolio | calculable_slices | none
|
|
64
|
+
metrics: {spend: scenario_metric, revenue: scenario_metric, roas: scenario_metric}
|
|
65
|
+
warning: string | null
|
|
44
66
|
frozen_at: timestamp
|
|
45
67
|
```
|
|
46
68
|
|
|
@@ -51,9 +73,10 @@ Do not include `immutable_fingerprint` in the tool input; a draft that carries o
|
|
|
51
73
|
Almost none of the artifact above is a decision. The decision is the allocation
|
|
52
74
|
and the ROAS assumption behind each slice; revenue, the ROAS write-back, the
|
|
53
75
|
consolidated roll-up, every unit and window string, and `frozen_at` all follow
|
|
54
|
-
from those by arithmetic. Write the plan and let `
|
|
55
|
-
the rest
|
|
56
|
-
instead of true if you typed
|
|
76
|
+
from those by arithmetic. Write the plan and let `fpa_forecast_finalize` derive
|
|
77
|
+
the rest, choose the lifecycle role from time, and commit it — it makes the
|
|
78
|
+
identities the commit checks true by construction instead of true if you typed
|
|
79
|
+
them correctly.
|
|
57
80
|
|
|
58
81
|
```yaml
|
|
59
82
|
# artifacts/forecast_plan.json
|
|
@@ -89,15 +112,20 @@ Then:
|
|
|
89
112
|
|
|
90
113
|
```
|
|
91
114
|
write artifacts/forecast_plan.json
|
|
92
|
-
|
|
93
|
-
fpa_artifact_commit { "artifact": <the artifact compose returned> }
|
|
115
|
+
fpa_forecast_finalize { "plan_path": "artifacts/forecast_plan.json", "scope_id": "<exact scope>", "cycle_id": "<exact cycle>" }
|
|
94
116
|
```
|
|
95
117
|
|
|
118
|
+
The finalizer derives `next_plan` before the target period, `eac` during it,
|
|
119
|
+
and `backtest` after it. The model does not pass or translate `forecast_role`.
|
|
120
|
+
|
|
96
121
|
Rules the plan has to respect, because compose enforces them:
|
|
97
122
|
|
|
98
123
|
- **Spend is a decision, not a prediction.** It is identical in all three scenarios, so a slice carries one `approved_spend`. Only revenue moves, through `roas`.
|
|
99
124
|
- **A stopped slice has no ROAS.** `approved_spend: 0` means revenue 0 and `roas` null in all three scenarios — the contract derives ROAS as revenue/spend and calls a zero denominator null.
|
|
100
|
-
- **Slice keys
|
|
125
|
+
- **Slice keys are checked as real triples.** `app_code`, `platform`, and `media_source` must occur together in `ua_spend`; validating three independent value lists is insufficient. A mismatch is isolated as a data-quality limit: approved spend remains in the allocation and consolidated spend, while affected revenue/ROAS are `NULL`. The single-placeholder App axis for a scoped-out portfolio remains permitted.
|
|
126
|
+
- **Do not hide partiality.** Compose reports planned, calculable, and excluded spend. Its partial conclusion covers only calculable slices; remaining spend is never scaled to 100%, and partial ROAS is never labelled portfolio ROAS.
|
|
127
|
+
- **Data quality does not weaken governance.** Missing/mismatched approval, duplicate or negative allocation, invalid period/currency/forecast role, and budget reconciliation failures still block.
|
|
128
|
+
- **Describe other upstream limits actionably.** For non-identity defects, each `unsupported_metrics` item should name the affected metric/scope, evidence, remediation, and data owner. Dashboard publication keeps these items visible even when `data_quality.status` is otherwise complete.
|
|
101
129
|
- **`status` and `unsupported_metrics` must agree.** `complete` requires an empty list; `complete_with_limits` requires a non-empty one. Scope excluded upstream is neither.
|
|
102
130
|
- **Do not hand-compute anything derived.** A ROAS rounded to four decimals misses the commit tolerance and costs a re-draft.
|
|
103
131
|
|
|
@@ -32,9 +32,12 @@ preserve every other module revision.
|
|
|
32
32
|
3. Stop and wait for the dashboard action. The Web host sends a canonical
|
|
33
33
|
decision message back to the bound original session. In that session call
|
|
34
34
|
`fpa_strategy_decision_commit` with the exact `action_id`.
|
|
35
|
-
4. On `confirm`, run `fpa-forecast-freeze
|
|
36
|
-
|
|
37
|
-
`
|
|
35
|
+
4. On `confirm`, run `fpa-forecast-freeze`. For an operating Forecast, preview
|
|
36
|
+
then publish `next-forecast` with `fpa_dashboard_publish_forecast`. If the
|
|
37
|
+
finalizer returns `forecast_role: backtest` and a `not_applicable` dashboard
|
|
38
|
+
handoff, stop after reporting its immutable ref; do not attempt operating
|
|
39
|
+
publication unless the user explicitly requested the separate backtest
|
|
40
|
+
display path.
|
|
38
41
|
5. On `request_changes`, pass the verbatim feedback back into strategy planning,
|
|
39
42
|
publish the replacement `next-strategy` module, and wait for a new decision.
|
|
40
43
|
|
|
@@ -57,19 +60,21 @@ only part of this tuple or fall back to a mutable current pointer.
|
|
|
57
60
|
This path remains only for legacy execution-evidence refreshes. It must not be
|
|
58
61
|
used to replace review, strategy, or forecast modules.
|
|
59
62
|
|
|
60
|
-
1.
|
|
63
|
+
1. For an operating forecast, take the exact `artifact_ref` from a deferred
|
|
64
|
+
`fpa_forecast_finalize.dashboard_handoff` (or the compatible
|
|
65
|
+
`fpa_artifact_commit.dashboard_handoff` path). A backtest handoff is
|
|
66
|
+
`not_applicable` and must not be queued automatically.
|
|
61
67
|
2. Call `fpa_dashboard_refresh_queue` with `action: enqueue_artifact` and that
|
|
62
68
|
ref. Do not restate scope, cycle, role, or upstream refs; the package resolves
|
|
63
69
|
them from the immutable ledger.
|
|
64
70
|
3. If the result is `enqueued` or `already_pending` and the current task expects
|
|
65
71
|
the dashboard immediately, call one bounded `drain`, then verify with
|
|
66
72
|
`fpa_dashboard_status`.
|
|
67
|
-
4.
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
ledger without changing the dashboard.
|
|
73
|
+
4. A compatible legacy `fpa_artifact_commit` handoff may still return
|
|
74
|
+
`not_applicable` for a `backtest`; do not treat that as a failed artifact or
|
|
75
|
+
retry it. If the user explicitly asked to display that backtest, continue
|
|
76
|
+
through the exact backtest display path below. Otherwise report that it
|
|
77
|
+
remains in the ledger without changing the operating dashboard.
|
|
73
78
|
|
|
74
79
|
### Human-requested rebuild
|
|
75
80
|
|