@viccydev/pi-fpa 0.7.0 → 0.7.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 +29 -8
- package/extensions/fpa-routing-guard/index.ts +220 -0
- package/package.json +3 -2
- package/prompts/fpa-plan-cycle.md +15 -15
- package/prompts/fpa-review-cycle.md +10 -6
- package/skills/fpa-apply-core-rules/SKILL.md +35 -1
- package/skills/fpa-apply-core-rules/references/core-rules.md +23 -7
- package/skills/fpa-execute-approved-strategy/SKILL.md +5 -1
- package/skills/fpa-forecast-approved-strategy/SKILL.md +16 -2
- package/skills/fpa-plan-cycle/SKILL.md +5 -2
- package/skills/fpa-review-cycle/SKILL.md +6 -1
- package/skills/fpa-review-strategy/SKILL.md +4 -1
package/README.md
CHANGED
|
@@ -2,14 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
面向 Pi 的完整 FP&A 周期资源包,目标运行时为 `@earendil-works/pi-coding-agent` 0.84.1。
|
|
4
4
|
|
|
5
|
-
本包分发 Prompt Template、Skill
|
|
5
|
+
本包分发 Prompt Template、Skill、参考合同,以及 Graph 路由护栏、数据读取、规范化 Artifact 和看板投影四个 Extension。包内不含业务数据与模型凭证,也不实现工作流状态机;它声明并在工具调用边界执行主 Agent 必须遵守的 Graph-first 路由契约。Graph 的定义、状态、多 Agent 隔离、人工审批和真实外部执行仍由宿主运行时或独立工作流承担。
|
|
6
6
|
|
|
7
7
|
## 包含的资源
|
|
8
8
|
|
|
9
9
|
Prompt Template:
|
|
10
10
|
|
|
11
|
-
- `/fpa-plan-cycle
|
|
12
|
-
- `/fpa-review-cycle
|
|
11
|
+
- `/fpa-plan-cycle`:通过 `fpa-strategy-planning` Graph 完成 Actuals 诊断、驱动分析、策略模拟、推荐和独立复核,形成 `reviewed_strategy_handoff` 后停止;不跨越人工批准或冻结正式预测。
|
|
12
|
+
- `/fpa-review-cycle`:在精确 Forecast/Execution refs 和新周期 Actuals 到达后,通过 `fpa-cycle-review` Graph 形成周期复盘。
|
|
13
13
|
|
|
14
14
|
Skill:
|
|
15
15
|
|
|
@@ -27,6 +27,27 @@ Skill:
|
|
|
27
27
|
|
|
28
28
|
执行 Skill 设置了 `disable-model-invocation: true`,不会出现在模型可主动调用的 Skill 摘要中,也没有对应 Prompt。只有用户显式输入 `/skill:fpa-execute-approved-strategy` 才能加载它;即使显式加载,缺少精确批准、单独执行授权、真实 adapter、幂等键或成功 preflight 时也必须保持 `blocked`,不得产生外部变更。
|
|
29
29
|
|
|
30
|
+
## Graph-first 路由
|
|
31
|
+
|
|
32
|
+
主 Agent 同时具备 `graph_list` 和 `graph_run` 时,任何跨两个或更多 FP&A
|
|
33
|
+
阶段的请求都必须先查询 Graph catalog,再运行当前第一个满足入口条件的
|
|
34
|
+
Graph;在此之前不得调用 `fpa_*` 工具或写 Artifact。四个工作流的边界为:
|
|
35
|
+
|
|
36
|
+
- `fpa-strategy-planning`:规划至 `reviewed_strategy_handoff` 后停止。
|
|
37
|
+
- `fpa-forecast-freeze`:只接受精确 handoff,经人工批准后冻结 Forecast。
|
|
38
|
+
- `fpa-strategy-execution`:只接受精确 committed Forecast ref 和单独执行授权。
|
|
39
|
+
- `fpa-cycle-review`:只接受精确 Forecast/Execution refs 与新 Actuals。
|
|
40
|
+
|
|
41
|
+
Graph 的业务阻塞不能触发父 Agent 降级执行。Graph 工具不可用、catalog 不存在
|
|
42
|
+
匹配 Graph 或匹配 Graph 无法加载时,多阶段入口保持 `blocked`;用户必须另行
|
|
43
|
+
显式请求一个隔离阶段,父 Agent 不能自动把完整工作流改成本地串行执行。Graph
|
|
44
|
+
节点只执行分配给自己的阶段,不得递归启动另一个 Graph。
|
|
45
|
+
|
|
46
|
+
`fpa-routing-guard` Extension 会在 Pi 的 `tool_call` 执行前落实这条边界:
|
|
47
|
+
它从展开后的请求识别多阶段 FP&A 意图,要求目标 Graph 与请求阶段一致,并
|
|
48
|
+
阻断父 Agent 的提前 `fpa_*` 调用和 `artifacts/` 写入。Graph worker 没有
|
|
49
|
+
`graph_list`/`graph_run` 时不会激活该护栏,因此节点仍可执行被分配的单一阶段。
|
|
50
|
+
|
|
30
51
|
## 数据 Extension(fpa-data)
|
|
31
52
|
|
|
32
53
|
`extensions/fpa-data` 注册五个只读工具,连接 Supabase 数据集市:
|
|
@@ -127,12 +148,12 @@ pi list
|
|
|
127
148
|
团队分发建议使用固定 Git tag:
|
|
128
149
|
|
|
129
150
|
```bash
|
|
130
|
-
pi install git:github.com/linyqh/pi-fpa@v0.7.
|
|
151
|
+
pi install git:github.com/linyqh/pi-fpa@v0.7.1
|
|
131
152
|
```
|
|
132
153
|
|
|
133
154
|
## 发布到 npm
|
|
134
155
|
|
|
135
|
-
发布动作由 GitHub Release 触发。Release 标签必须严格使用 `v<package.json version>`,例如版本 `0.7.
|
|
156
|
+
发布动作由 GitHub Release 触发。Release 标签必须严格使用 `v<package.json version>`,例如版本 `0.7.1` 对应 `v0.7.1`。工作流会检出该标签,执行 `npm ci`、`npm test` 和包内容预检,全部通过后发布公开包 `@viccydev/pi-fpa`。普通 Release 发布到 `latest`,Prerelease 发布到 `next`。
|
|
136
157
|
|
|
137
158
|
发布认证使用 npm Trusted Publishing / OIDC,不使用长期 npm Token。npm 包后台的 Trusted Publisher 配置为:
|
|
138
159
|
|
|
@@ -148,14 +169,14 @@ pi install git:github.com/linyqh/pi-fpa@v0.7.0
|
|
|
148
169
|
|
|
149
170
|
```text
|
|
150
171
|
/fpa-plan-cycle /path/to/project 2026-Q3 "按 App、Store、Channel Group 规划;预算上限见 planning input"
|
|
151
|
-
/fpa-review-cycle /path/to/project 2026-Q3 "
|
|
172
|
+
/fpa-review-cycle /path/to/project 2026-Q3 "使用精确 forecast/execution refs 和新到达的 Actuals snapshot"
|
|
152
173
|
/skill:fpa-refresh-dashboard "预览并发布当前项目的预测闭环看板"
|
|
153
174
|
```
|
|
154
175
|
|
|
155
|
-
|
|
176
|
+
规划入口停在 `reviewed_strategy_handoff`,不会等待人工批准、冻结 Forecast 或进入真实策略执行。后续分别由 `fpa-forecast-freeze` 和 `fpa-strategy-execution` Graph 承担。若宿主没有 Graph 能力、且用户只请求一个隔离执行阶段,才可在单独、已授权的运行中显式调用:
|
|
156
177
|
|
|
157
178
|
```text
|
|
158
|
-
/skill:fpa-execute-approved-strategy <exact
|
|
179
|
+
/skill:fpa-execute-approved-strategy <exact committed forecast ref and execution scope>
|
|
159
180
|
```
|
|
160
181
|
|
|
161
182
|
## 资源迁移注意
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
BeforeAgentStartEvent,
|
|
3
|
+
ExtensionAPI,
|
|
4
|
+
ToolCallEvent,
|
|
5
|
+
ToolResultEvent,
|
|
6
|
+
} from "@earendil-works/pi-coding-agent";
|
|
7
|
+
|
|
8
|
+
const GRAPH_TOOLS = ["graph_list", "graph_run"] as const;
|
|
9
|
+
const FP_AND_A_GRAPHS = [
|
|
10
|
+
"fpa-strategy-planning",
|
|
11
|
+
"fpa-forecast-freeze",
|
|
12
|
+
"fpa-strategy-execution",
|
|
13
|
+
"fpa-cycle-review",
|
|
14
|
+
] as const;
|
|
15
|
+
type FpaGraph = (typeof FP_AND_A_GRAPHS)[number];
|
|
16
|
+
|
|
17
|
+
interface RoutingState {
|
|
18
|
+
requiredGraph?: FpaGraph;
|
|
19
|
+
catalogLoaded: boolean;
|
|
20
|
+
availableGraphs: Set<string>;
|
|
21
|
+
graphRunStarted: boolean;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function emptyState(): RoutingState {
|
|
25
|
+
return {
|
|
26
|
+
catalogLoaded: false,
|
|
27
|
+
availableGraphs: new Set(),
|
|
28
|
+
graphRunStarted: false,
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function includesAny(prompt: string, patterns: RegExp[]): boolean {
|
|
33
|
+
return patterns.some((pattern) => pattern.test(prompt));
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function isMetaRequest(prompt: string): boolean {
|
|
37
|
+
return includesAny(prompt, [
|
|
38
|
+
/(?:解释|说明).*(?:为何|为什么)/,
|
|
39
|
+
/(?:为何|为什么).*(?:未|没|没有|不)/,
|
|
40
|
+
/(?:排查|修复|分析).*(?:bug|路由|工作流未运行|工具顺序|代码问题)/i,
|
|
41
|
+
/(?:比较|对比|说明|解释).*(?:graph|工作流).*(?:职责|区别|差异|定义|作用|用途)/i,
|
|
42
|
+
/(?:修改|编辑|更新|创建|设计|删除).*(?:graph|工作流|fpa-(?:strategy|forecast|cycle)).*(?:定义|节点|配置|spec|graph)?/i,
|
|
43
|
+
/(?:查询|查看|解释|计算).*(?:不做|不要|无需).*(?:规划|预测|执行|复盘|推荐)/i,
|
|
44
|
+
/(?:不要|无需|不需|禁止)(?:执行|运行|调用).*(?:工作流|graph|fpa_)/i,
|
|
45
|
+
/(?:do not|don't|without) (?:run|execute|call)/i,
|
|
46
|
+
]);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function isSingleInformationalRequest(prompt: string): boolean {
|
|
50
|
+
const asksForOneFact = /(?:是什么|什么意思|定义|查询|查看).*(?:ROAS|Revenue|Actuals?|Forecast|FP&A|\bFPA\b|\bUA\b)|(?:ROAS|Revenue|Actuals?|Forecast|FP&A|\bFPA\b|\bUA\b).*(?:是什么|什么意思|定义)/i.test(prompt);
|
|
51
|
+
if (!asksForOneFact) return false;
|
|
52
|
+
return !/(?:然后|接着|随后|后(?:再)?|并(?:且)?|制定|规划|预算方案|分析驱动|策略建议|独立审核|冻结|执行|复盘)/i.test(prompt);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function isExplicitIsolatedPhase(prompt: string): boolean {
|
|
56
|
+
const skill = prompt.match(/^<skill name="(fpa-[^"]+)"/);
|
|
57
|
+
if (skill && skill[1] !== "fpa-apply-core-rules") return true;
|
|
58
|
+
if (!/(?:^|[。;;]\s*)(?:请)?(?:本次)?(?:只|仅)|\bonly\b/i.test(prompt)) return false;
|
|
59
|
+
return !/(?:然后|接着|随后|再进入|后再|直至|全流程|完整流程|end[- ]to[- ]end)/i.test(prompt);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function hasFpaIntent(prompt: string): boolean {
|
|
63
|
+
if (includesAny(prompt, [/分析数据.*(?:方案|计划)/, /下一周期.*(?:方案|计划)/, /完整.*(?:流程|工作流)/])) return true;
|
|
64
|
+
const businessSignals = [
|
|
65
|
+
/FP&A|\bFPA\b/i,
|
|
66
|
+
/\bUA\b/i,
|
|
67
|
+
/ROAS/i,
|
|
68
|
+
/Actuals?/i,
|
|
69
|
+
/\bspend\b/i,
|
|
70
|
+
/预算|budget/i,
|
|
71
|
+
/规划|\bplan(?:ning)?\b/i,
|
|
72
|
+
/投放|campaign/i,
|
|
73
|
+
/策略|strategy/i,
|
|
74
|
+
/预测|forecast/i,
|
|
75
|
+
/复盘|variance/i,
|
|
76
|
+
/归因|attribution/i,
|
|
77
|
+
/收入|revenue/i,
|
|
78
|
+
/花费|cost/i,
|
|
79
|
+
];
|
|
80
|
+
return businessSignals.filter((pattern) => pattern.test(prompt)).length >= 2;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export function classifyFpaGraph(prompt: string): FpaGraph | undefined {
|
|
84
|
+
const normalized = prompt.toLowerCase();
|
|
85
|
+
if (isMetaRequest(normalized) || isExplicitIsolatedPhase(prompt) || isSingleInformationalRequest(prompt)) return undefined;
|
|
86
|
+
|
|
87
|
+
for (const graph of FP_AND_A_GRAPHS) {
|
|
88
|
+
if (
|
|
89
|
+
normalized.includes(graph) &&
|
|
90
|
+
/(?:运行|执行|启动|调用|使用|通过|\brun\b|\bexecute\b|\bstart\b)/i.test(normalized)
|
|
91
|
+
) return graph;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const exactRef = /(?:精确|exact|immutable|committed|已提交|不可变).{0,24}(?:ref|引用)/i;
|
|
95
|
+
if (
|
|
96
|
+
includesAny(normalized, [/复盘/, /cycle[ -]?review/, /variance/, /偏差/]) &&
|
|
97
|
+
includesAny(normalized, [/actuals?/, /实际/]) &&
|
|
98
|
+
/(?:forecast|预测).{0,32}(?:execution|执行).{0,32}(?:refs?|引用)|(?:refs?|引用).{0,32}(?:forecast|预测).{0,32}(?:execution|执行)/i.test(normalized)
|
|
99
|
+
) {
|
|
100
|
+
return "fpa-cycle-review";
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
if (
|
|
104
|
+
/reviewed_strategy_handoff|(?:精确|exact|不可变).{0,24}handoff/i.test(normalized) &&
|
|
105
|
+
includesAny(normalized, [/冻结.*预测/, /正式预测/, /approved.*forecast/, /forecast.*freeze/])
|
|
106
|
+
) {
|
|
107
|
+
return "fpa-forecast-freeze";
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
if (
|
|
111
|
+
includesAny(normalized, [/执行/, /execute/, /投放变更/, /external mutation/]) &&
|
|
112
|
+
includesAny(normalized, [/授权/, /获批/, /approved/, /批准/]) &&
|
|
113
|
+
/(?:forecast|预测)/i.test(normalized) && exactRef.test(normalized)
|
|
114
|
+
) {
|
|
115
|
+
return "fpa-strategy-execution";
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
return hasFpaIntent(normalized) ? "fpa-strategy-planning" : undefined;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function contentText(event: ToolResultEvent): string {
|
|
122
|
+
return event.content
|
|
123
|
+
.filter((item): item is Extract<(typeof event.content)[number], { type: "text" }> => item.type === "text")
|
|
124
|
+
.map((item) => item.text)
|
|
125
|
+
.join("\n");
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function catalogNames(event: ToolResultEvent): string[] {
|
|
129
|
+
const details = event.details;
|
|
130
|
+
if (details && typeof details === "object" && "names" in details) {
|
|
131
|
+
const names = (details as { names?: unknown }).names;
|
|
132
|
+
if (Array.isArray(names)) return names.filter((name): name is string => typeof name === "string");
|
|
133
|
+
}
|
|
134
|
+
return FP_AND_A_GRAPHS.filter((graph) => contentText(event).includes(graph));
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function artifactWrite(event: ToolCallEvent): boolean {
|
|
138
|
+
if (event.toolName === "write" || event.toolName === "edit") {
|
|
139
|
+
const path = "path" in event.input && typeof event.input.path === "string" ? event.input.path : "";
|
|
140
|
+
return /(?:^|[\\/])artifacts?[\\/]/i.test(path);
|
|
141
|
+
}
|
|
142
|
+
if (event.toolName !== "bash") return false;
|
|
143
|
+
const command = "command" in event.input && typeof event.input.command === "string" ? event.input.command : "";
|
|
144
|
+
return /artifacts?[\\/]/i.test(command);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
function routingInstruction(graph: FpaGraph): string {
|
|
148
|
+
return [
|
|
149
|
+
"FP&A runtime routing guard is active for this request.",
|
|
150
|
+
`The first eligible workflow is ${graph}.`,
|
|
151
|
+
"Call graph_list first, then graph_run with that exact graph and the complete immutable context.",
|
|
152
|
+
"Do not call fpa_* tools or write artifacts in the parent agent; Graph nodes own phase execution.",
|
|
153
|
+
].join(" ");
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
export default function fpaRoutingGuardExtension(pi: ExtensionAPI): void {
|
|
157
|
+
let state = emptyState();
|
|
158
|
+
|
|
159
|
+
pi.on("before_agent_start", (event: BeforeAgentStartEvent) => {
|
|
160
|
+
const previousGraph = state.requiredGraph;
|
|
161
|
+
state = emptyState();
|
|
162
|
+
const active = new Set(pi.getActiveTools());
|
|
163
|
+
if (!GRAPH_TOOLS.every((tool) => active.has(tool))) return;
|
|
164
|
+
|
|
165
|
+
const classified = classifyFpaGraph(event.prompt);
|
|
166
|
+
const isContinuation = /^(?:继续|接着|下一步|continue|go on)[。.!!\s]*$/i.test(event.prompt.trim());
|
|
167
|
+
const requiredGraph = classified ?? (isContinuation ? previousGraph : undefined);
|
|
168
|
+
if (!requiredGraph) return;
|
|
169
|
+
state.requiredGraph = requiredGraph;
|
|
170
|
+
return { systemPrompt: `${event.systemPrompt}\n\n${routingInstruction(requiredGraph)}` };
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
pi.on("tool_call", (event: ToolCallEvent) => {
|
|
174
|
+
const graph = state.requiredGraph;
|
|
175
|
+
if (!graph) return;
|
|
176
|
+
|
|
177
|
+
if (event.toolName === "graph_list") return;
|
|
178
|
+
if (event.toolName === "graph_run") {
|
|
179
|
+
if (!state.catalogLoaded) {
|
|
180
|
+
return { block: true, reason: `Call graph_list before graph_run for ${graph}.` };
|
|
181
|
+
}
|
|
182
|
+
const requested = "graph" in event.input ? event.input.graph : undefined;
|
|
183
|
+
if (requested !== graph) {
|
|
184
|
+
return { block: true, reason: `Run the first eligible ${graph} Graph; received ${String(requested)}.` };
|
|
185
|
+
}
|
|
186
|
+
if (!state.availableGraphs.has(graph)) {
|
|
187
|
+
return {
|
|
188
|
+
block: true,
|
|
189
|
+
reason: `${graph} is not available. Return blocked; local work requires a new, explicit isolated-phase request.`,
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
state.graphRunStarted = true;
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
if (!event.toolName.startsWith("fpa_") && !artifactWrite(event)) return;
|
|
197
|
+
if (!state.catalogLoaded) {
|
|
198
|
+
return { block: true, reason: `Call graph_list before any FP&A phase tool for ${graph}.` };
|
|
199
|
+
}
|
|
200
|
+
if (!state.availableGraphs.has(graph)) {
|
|
201
|
+
return {
|
|
202
|
+
block: true,
|
|
203
|
+
reason: `${graph} is unavailable. Return blocked; do not automatically fall back to a multi-stage local workflow.`,
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
if (!state.graphRunStarted) {
|
|
207
|
+
return { block: true, reason: `Call graph_run with ${graph} before any FP&A phase tool.` };
|
|
208
|
+
}
|
|
209
|
+
return {
|
|
210
|
+
block: true,
|
|
211
|
+
reason: `The ${graph} Graph owns FP&A phase work; the parent agent must not execute FP&A phase tools or write artifacts.`,
|
|
212
|
+
};
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
pi.on("tool_result", (event: ToolResultEvent) => {
|
|
216
|
+
if (!state.requiredGraph || event.toolName !== "graph_list" || event.isError) return;
|
|
217
|
+
state.catalogLoaded = true;
|
|
218
|
+
state.availableGraphs = new Set(catalogNames(event));
|
|
219
|
+
});
|
|
220
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@viccydev/pi-fpa",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.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",
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
"fpa-dashboard-worker": "./bin/fpa-dashboard-worker.mjs"
|
|
31
31
|
},
|
|
32
32
|
"scripts": {
|
|
33
|
-
"test": "node tests/package-structure.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/dashboard-actuals.test.mjs tests/dashboard-projector.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",
|
|
33
|
+
"test": "node tests/package-structure.test.mjs && node tests/workflow-routing.test.mjs && node --test tests/workflow-routing-guard.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/dashboard-actuals.test.mjs tests/dashboard-projector.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",
|
|
34
34
|
"test:structure": "node tests/package-structure.test.mjs",
|
|
35
35
|
"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/dashboard-actuals.test.mjs tests/dashboard-projector.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",
|
|
36
36
|
"test:loader": "node tests/pi-loader-smoke.mjs && node tests/worker-loader-smoke.mjs",
|
|
@@ -58,6 +58,7 @@
|
|
|
58
58
|
"./skills"
|
|
59
59
|
],
|
|
60
60
|
"extensions": [
|
|
61
|
+
"./extensions/fpa-routing-guard/index.ts",
|
|
61
62
|
"./extensions/fpa-data/index.ts",
|
|
62
63
|
"./extensions/fpa-artifacts/index.ts",
|
|
63
64
|
"./extensions/fpa-dashboard/index.ts"
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
---
|
|
2
|
-
description:
|
|
2
|
+
description: 通过 Graph 启动 FP&A 策略规划,并在复核交接点停止
|
|
3
3
|
argument-hint: "<project-root> <cycle-id> [instructions]"
|
|
4
4
|
---
|
|
5
5
|
|
|
@@ -14,25 +14,25 @@ argument-hint: "<project-root> <cycle-id> [instructions]"
|
|
|
14
14
|
- 明确周期的开始时间、结束时间、业务时区、报告币种、比较基线,以及 App、Store、Product Group、Channel Group 范围。不能从 `$2` 的名称暗自推断日期边界。
|
|
15
15
|
- 关键输入缺失时列出缺口并保持 `blocked`;不得编造字段、表、数据、批准或 Artifact。
|
|
16
16
|
|
|
17
|
-
##
|
|
17
|
+
## 强制 Graph 路由
|
|
18
18
|
|
|
19
|
-
|
|
19
|
+
先读取 `fpa-apply-core-rules`。如果当前 Agent 提供 `graph_list` 和
|
|
20
|
+
`graph_run`,必须先调用 `graph_list`,确认 `fpa-strategy-planning` 后再以
|
|
21
|
+
完整用户目标、已解析的项目根、周期边界、业务范围和数据上下文调用
|
|
22
|
+
`graph_run`。在这两步之前不得直接调用任何 `fpa_*` 工具或写 Artifact,
|
|
23
|
+
父 Agent 也不得自行串行模拟 Graph 内的阶段。
|
|
20
24
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
4. `fpa-recommend-strategy`
|
|
25
|
-
5. `fpa-review-strategy`
|
|
26
|
-
6. `fpa-forecast-approved-strategy`
|
|
27
|
-
|
|
28
|
-
不要一次性加载所有 Skill,也不要在本 Prompt 中复述它们的完整规则。每一阶段只消费已完成的上游 Artifact,并按对应 `references/artifact-contract.md` 输出。
|
|
25
|
+
如果匹配 Graph 不在 catalog 中或无法加载,列出缺口并保持 `blocked`;不得把
|
|
26
|
+
本多阶段入口自动降级为父 Agent 本地执行。用户之后可另行显式请求一个隔离阶段。
|
|
27
|
+
Graph 返回业务阻塞同样不构成降级理由。所需上下文缺失时也保持 `blocked`。
|
|
29
28
|
|
|
30
29
|
## 不可跳过的停点
|
|
31
30
|
|
|
32
31
|
- 策略推荐者不得批准或独立复核自己的提案。若宿主不能证明复核上下文与提案作者独立,复核阶段必须报告 `blocked`。
|
|
33
|
-
-
|
|
34
|
-
-
|
|
35
|
-
-
|
|
32
|
+
- 独立复核完成后,`fpa-strategy-planning` 必须生成精确绑定提案与复核版本的 `reviewed_strategy_handoff`,然后停止。
|
|
33
|
+
- 聊天中的认可、模型自我确认或 Forecast 授权都不等于策略执行授权。
|
|
34
|
+
- 人工批准与正式预测属于后续独立的 `fpa-forecast-freeze` Graph;父 Agent 不得在本入口中继续执行。
|
|
35
|
+
- 本入口不得加载 Forecast 或策略执行 Skill,不得声称已经冻结 Forecast 或修改外部投放状态。
|
|
36
36
|
|
|
37
37
|
## 全局计算与证据底线
|
|
38
38
|
|
|
@@ -41,4 +41,4 @@ argument-hint: "<project-root> <cycle-id> [instructions]"
|
|
|
41
41
|
- 区分 observed、assumed、modeled、judgment 和 approved;相关性不得冒充因果关系。
|
|
42
42
|
- 每个结论记录实际表、字段、grain、join key、时间范围、`data_as_of`、版本和质量限制。
|
|
43
43
|
|
|
44
|
-
最终交付应包含 `planning_brief`、`actuals_snapshot`、`data_issue_report`、`driver_analysis`、`strategy_scenarios`、`strategy_proposal`、独立 `strategy_review
|
|
44
|
+
最终交付应包含 `planning_brief`、`actuals_snapshot`、`data_issue_report`、`driver_analysis`、`strategy_scenarios`、`strategy_proposal`、独立 `strategy_review` 和 `reviewed_strategy_handoff`。任何未完成项都必须标注状态和阻塞原因。
|
|
@@ -10,17 +10,21 @@ argument-hint: "<project-root> <cycle-id> [instructions]"
|
|
|
10
10
|
## 输入解析
|
|
11
11
|
|
|
12
12
|
- 把 `$1` 作为项目根并确认它真实存在。
|
|
13
|
-
-
|
|
13
|
+
- 找到该周期精确不可变的 `approved_cycle_forecast` ref、同一周期精确不可变的 `execution_receipt` ref,以及覆盖同一业务周期的新 Actuals snapshot;不得用 mutable current pointer 或复制的 payload 代替。
|
|
14
14
|
- 默认从项目根解析 `LOCAL_FPA_MART_FIELD_CATALOG.md` 和 `LOCAL_FPA_MART_SCHEMA.sql`;补充要求提供其他显式路径时使用显式路径。
|
|
15
15
|
- 明确复盘周期的精确起止时间、业务时区、币种和业务范围。若 Forecast 与 Actuals 不可比,缩窄结论或保持 `blocked`,不得修写历史 Forecast。
|
|
16
16
|
|
|
17
|
-
##
|
|
17
|
+
## 强制 Graph 路由
|
|
18
18
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
19
|
+
先读取 `fpa-apply-core-rules`。如果当前 Agent 提供 `graph_list` 和
|
|
20
|
+
`graph_run`,必须先调用 `graph_list`,确认 `fpa-cycle-review` 后再以完整用户
|
|
21
|
+
目标、精确 Forecast ref、精确 Execution ref 和新 Actuals 上下文调用
|
|
22
|
+
`graph_run`。在这两步之前不得直接调用任何 `fpa_*` 工具或写 Artifact,
|
|
23
|
+
父 Agent 不得自行执行 Actuals 诊断或复盘阶段。
|
|
22
24
|
|
|
23
|
-
|
|
25
|
+
如果匹配 Graph 不在 catalog 中或无法加载,列出缺口并保持 `blocked`;不得把
|
|
26
|
+
本多阶段入口自动降级为父 Agent 本地执行。用户之后可另行显式请求一个隔离阶段。
|
|
27
|
+
Graph 返回业务阻塞同样不构成降级理由。关键 ref 或上下文缺失时保持 `blocked`。
|
|
24
28
|
|
|
25
29
|
## 复盘底线
|
|
26
30
|
|
|
@@ -7,9 +7,43 @@ description: Apply the shared period, source-grain, metric, quality, approval, a
|
|
|
7
7
|
|
|
8
8
|
Use this skill together with the phase-specific FP&A skill. It is the common contract, not a complete workflow by itself.
|
|
9
9
|
|
|
10
|
+
## Main-Agent Graph routing gate
|
|
11
|
+
|
|
12
|
+
Apply this gate before reading phase references, calling business tools, or
|
|
13
|
+
writing artifacts. It applies only when the current agent exposes both
|
|
14
|
+
`graph_list` and `graph_run`.
|
|
15
|
+
|
|
16
|
+
1. If the user's goal spans two or more FP&A phases, call `graph_list` before
|
|
17
|
+
any `fpa_*` tool or artifact write. A phase handoff in a Skill is not
|
|
18
|
+
permission for the parent agent to emulate the remaining phases itself.
|
|
19
|
+
2. Select the first currently eligible workflow from the catalog:
|
|
20
|
+
|
|
21
|
+
| Eligible request and evidence | Required Graph |
|
|
22
|
+
| --- | --- |
|
|
23
|
+
| New or changed objective requiring planning, Actuals diagnosis, driver analysis, scenarios, recommendation, or independent review | `fpa-strategy-planning` |
|
|
24
|
+
| Ready `reviewed_strategy_handoff` plus a request for human approval and an official forecast | `fpa-forecast-freeze` |
|
|
25
|
+
| Exact committed approved Forecast ref plus an authorized execution request | `fpa-strategy-execution` |
|
|
26
|
+
| Exact Forecast and Execution refs plus newly arrived comparable Actuals | `fpa-cycle-review` |
|
|
27
|
+
|
|
28
|
+
3. Call `graph_run` with the user's complete goal and the exact immutable
|
|
29
|
+
context required by that Graph. The parent agent must not reproduce its
|
|
30
|
+
nodes, pre-run their `fpa_*` calls, or automatically cross the next approval
|
|
31
|
+
or artifact handoff after the Graph returns.
|
|
32
|
+
4. If required context is missing or ambiguous, return `blocked` and request
|
|
33
|
+
the exact identity or ref. Do not fall back to direct phase execution.
|
|
34
|
+
|
|
35
|
+
Local phase execution is allowed when the user explicitly requests only one
|
|
36
|
+
isolated phase, or inside a Graph node. If Graph tools are unavailable or the
|
|
37
|
+
matching Graph is absent/unloadable for a multi-stage request, return `blocked`;
|
|
38
|
+
do not automatically execute the workflow locally. The user may then submit a
|
|
39
|
+
new, explicit isolated-phase request. A business validation failure or a
|
|
40
|
+
blocked Graph result is never permission to fall back. A Graph node normally
|
|
41
|
+
receives only its assigned phase tools and Skills; it must execute that phase
|
|
42
|
+
locally and must not recursively start another Graph.
|
|
43
|
+
|
|
10
44
|
## Required procedure
|
|
11
45
|
|
|
12
|
-
1.
|
|
46
|
+
1. After the routing gate permits local phase work, read [core-rules.md](references/core-rules.md) before calculating or judging anything.
|
|
13
47
|
2. Identify the workflow phase and consume only artifacts that precede it.
|
|
14
48
|
3. Bind the requested business period to exact start and end timestamps, timezone, currency, App, Store, and Channel scope.
|
|
15
49
|
4. Check that every source grain can support the requested period. Never manufacture finer-grained results by prorating coarse facts.
|
|
@@ -2,19 +2,35 @@
|
|
|
2
2
|
|
|
3
3
|
## 1. Workflow and artifacts
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
Strategy-planning workflow (`fpa-strategy-planning`):
|
|
6
6
|
|
|
7
|
-
`planning_brief -> actuals_snapshot + data_issue_report -> driver_analysis -> strategy_scenarios -> strategy_proposal -> strategy_review ->
|
|
7
|
+
`planning_brief -> actuals_snapshot + data_issue_report -> driver_analysis -> strategy_scenarios -> strategy_proposal -> strategy_review -> reviewed_strategy_handoff`
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
This Graph stops after an independently reviewed strategy handoff. It does not
|
|
10
|
+
approve the strategy, create an official forecast, or execute the allocation.
|
|
10
11
|
|
|
11
|
-
|
|
12
|
+
Forecast-freeze workflow (`fpa-forecast-freeze`), entered only with that exact
|
|
13
|
+
ready handoff:
|
|
12
14
|
|
|
13
|
-
`strategy_approval
|
|
15
|
+
`reviewed_strategy_handoff -> human strategy_approval -> forecast_plan -> approved_cycle_forecast`
|
|
14
16
|
|
|
15
|
-
|
|
17
|
+
The forecast Graph owns approval recording, deterministic composition, and
|
|
18
|
+
artifact freezing. A rejected approval returns to a new
|
|
19
|
+
`fpa-strategy-planning` version rather than modifying the reviewed proposal in
|
|
20
|
+
place.
|
|
16
21
|
|
|
17
|
-
|
|
22
|
+
Execution is a separate `fpa-strategy-execution` workflow:
|
|
23
|
+
|
|
24
|
+
`exact approved_cycle_forecast ref -> execution_plan -> human confirmation -> execution_receipt`
|
|
25
|
+
|
|
26
|
+
Next-cycle review is a separate `fpa-cycle-review` workflow, triggered only
|
|
27
|
+
after new Actuals arrive:
|
|
28
|
+
|
|
29
|
+
`exact approved_cycle_forecast ref + exact execution_receipt ref + next actuals_snapshot -> cycle_review -> optional new planning cycle`
|
|
30
|
+
|
|
31
|
+
The main agent routes to only the currently eligible Graph. It must not emulate
|
|
32
|
+
multiple phases itself or automatically cross a human approval or immutable
|
|
33
|
+
artifact handoff.
|
|
18
34
|
|
|
19
35
|
Artifact ownership belongs to the workflow runtime or artifact store. An agent produces content and metadata; it must not invent a successful save, approval, or external action.
|
|
20
36
|
|
|
@@ -12,6 +12,8 @@ Load `$fpa-apply-core-rules` first. This skill is intentionally fail-closed and
|
|
|
12
12
|
|
|
13
13
|
Require all of the following before any external mutation:
|
|
14
14
|
|
|
15
|
+
- the exact committed `approved_cycle_forecast` ref returned by
|
|
16
|
+
`fpa-forecast-freeze`, not a mutable current pointer or a copied payload;
|
|
15
17
|
- explicit human `strategy_approval` for the exact `strategy_proposal` version;
|
|
16
18
|
- exact target accounts, App, Store, Channel, budget, period, and permitted operations;
|
|
17
19
|
- a named, currently available execution adapter/tool and its authentication context;
|
|
@@ -19,7 +21,9 @@ Require all of the following before any external mutation:
|
|
|
19
21
|
- an idempotency key or adapter-supported equivalent;
|
|
20
22
|
- a successful read-only preflight and, where supported, dry-run/preview.
|
|
21
23
|
|
|
22
|
-
If any requirement is missing
|
|
24
|
+
If any requirement is missing or the committed Forecast ref does not match the
|
|
25
|
+
approved scope and proposal lineage, return `blocked` and make no external
|
|
26
|
+
call. Never simulate an adapter or fabricate a receipt.
|
|
23
27
|
|
|
24
28
|
## Procedure
|
|
25
29
|
|
|
@@ -5,18 +5,24 @@ description: Produce and freeze the official operating forecast for the next bus
|
|
|
5
5
|
|
|
6
6
|
# FP&A Approved Strategy Forecast
|
|
7
7
|
|
|
8
|
-
Load `$fpa-apply-core-rules` first. This is
|
|
8
|
+
Load `$fpa-apply-core-rules` first. This is a post-approval phase inside the
|
|
9
|
+
separate forecast-freeze workflow, not a continuation that the planning parent
|
|
10
|
+
agent may start by itself.
|
|
9
11
|
|
|
10
12
|
## Entry gate
|
|
11
13
|
|
|
12
14
|
Require all of the following:
|
|
13
15
|
|
|
16
|
+
- the exact `reviewed_strategy_handoff` reference produced by
|
|
17
|
+
`fpa-strategy-planning`, including its bound proposal and review identities;
|
|
14
18
|
- the exact `strategy_proposal` version;
|
|
15
19
|
- a completed independent `strategy_review` supporting that version;
|
|
16
20
|
- explicit human `strategy_approval` identifying the same version and any conditions;
|
|
17
21
|
- the eligible data snapshot, assumptions, and model version used for forecasting.
|
|
18
22
|
|
|
19
23
|
If approval evidence is absent, ambiguous, expired, conditional but unmet, or refers to another strategy version, return `blocked` without forecasting.
|
|
24
|
+
If the handoff is missing or any supplied proposal/review identity differs
|
|
25
|
+
from it, return `blocked`; never reconstruct or substitute the handoff.
|
|
20
26
|
|
|
21
27
|
## Procedure
|
|
22
28
|
|
|
@@ -82,4 +88,12 @@ declaration upstream needs correcting.
|
|
|
82
88
|
|
|
83
89
|
## Completion
|
|
84
90
|
|
|
85
|
-
The
|
|
91
|
+
The forecast-freeze workflow ends only after `fpa_artifact_commit` returns
|
|
92
|
+
`status: committed`, an `immutable_fingerprint`, and (for assigned artifacts) a
|
|
93
|
+
`dashboard_handoff`. Commit freezes the business artifact but does not enqueue
|
|
94
|
+
or publish dashboard work. The calling main Agent owns that explicit handoff.
|
|
95
|
+
A Markdown report is optional context, not the frozen source of truth. Never
|
|
96
|
+
report a forecast as frozen on the strength of a written file alone — the file
|
|
97
|
+
is a draft until the tool returns a fingerprint. Execution, if requested, is a
|
|
98
|
+
separate `fpa-strategy-execution` Graph. Review waits for the next cycle's
|
|
99
|
+
Actuals and uses the separate `fpa-cycle-review` Graph.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: fpa-plan-cycle
|
|
3
|
-
description: Convert a business objective into a bounded FP&A and UA planning brief for one configurable business period.
|
|
3
|
+
description: Convert a business objective into a bounded FP&A and UA planning brief for one configurable business period. This is a single planning phase; for a multi-stage planning request, use the core Graph routing gate.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# FP&A Cycle Planning
|
|
@@ -26,4 +26,7 @@ Load `$fpa-apply-core-rules` first. Produce the goal contract that every downstr
|
|
|
26
26
|
|
|
27
27
|
## Handoff
|
|
28
28
|
|
|
29
|
-
|
|
29
|
+
When a Graph node assigns this phase, return the frozen `planning_brief` to the
|
|
30
|
+
Graph for `$fpa-diagnose-actuals`; pass the artifact, not an informal summary.
|
|
31
|
+
A main agent handling a multi-stage request must not continue the remaining
|
|
32
|
+
workflow in the parent agent.
|
|
@@ -9,7 +9,12 @@ Load `$fpa-apply-core-rules` first. This workflow is event-driven by a new perio
|
|
|
9
9
|
|
|
10
10
|
## Entry gate
|
|
11
11
|
|
|
12
|
-
Require the immutable `approved_cycle_forecast`
|
|
12
|
+
Require the exact immutable `approved_cycle_forecast` ref, the exact immutable
|
|
13
|
+
`execution_receipt` ref returned by `fpa-strategy-execution`, and a newly
|
|
14
|
+
diagnosed `actuals_snapshot` covering the same period and comparable scope.
|
|
15
|
+
All refs must carry matching scope and cycle lineage. If a ref is missing or
|
|
16
|
+
mismatched, data is incomplete, or source grain is incompatible, return
|
|
17
|
+
`blocked`; never resolve a mutable current pointer as a substitute.
|
|
13
18
|
|
|
14
19
|
## Procedure
|
|
15
20
|
|
|
@@ -25,4 +25,7 @@ Load `$fpa-apply-core-rules` first. The reviewer must be independent from the re
|
|
|
25
25
|
|
|
26
26
|
## Handoff
|
|
27
27
|
|
|
28
|
-
If the reviewer supports the proposal,
|
|
28
|
+
If the reviewer supports the proposal, return both artifacts to the Graph so it
|
|
29
|
+
can create the version-bound `reviewed_strategy_handoff`. Human approval and
|
|
30
|
+
`$fpa-forecast-approved-strategy` belong to the separate
|
|
31
|
+
`fpa-forecast-freeze` Graph; the reviewing agent must not start them.
|