@akagilnc/pi-workflow-roles 0.1.3789 → 0.1.3797
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 -1
- package/README.zh-CN.md +3 -1
- package/dist/acp-host/production-host.js +203 -42
- package/dist/collector-evidence.js +4 -1
- package/dist/collector-github.js +1 -0
- package/dist/collector-identity.js +11 -0
- package/dist/collector-ledger.js +73 -19
- package/dist/collector-role.js +82 -7
- package/dist/collector-tool-schemas.js +15 -2
- package/dist/headless-host/production-host.js +203 -42
- package/dist/pi/role-turn-host.js +1 -0
- package/dist/public-cli/invocation.js +15 -0
- package/dist/public-cli/main.js +43 -5
- package/dist/public-cli/option-definitions.js +14 -0
- package/dist/public-cli/run-lifecycle.js +8 -0
- package/package.json +1 -1
- package/souls/countersign.md +12 -10
- package/src/acp-host/role-envelope.ts +1 -0
- package/src/collector-evidence.ts +6 -1
- package/src/collector-github.ts +3 -0
- package/src/collector-identity.ts +14 -0
- package/src/collector-ledger.ts +95 -23
- package/src/collector-role.ts +81 -3
- package/src/collector-tool-schemas.ts +17 -2
- package/src/host-contracts.ts +2 -0
- package/src/pi/role-turn-host.ts +1 -0
- package/src/public-cli/collector-run.ts +2 -0
- package/src/public-cli/invocation.ts +21 -0
- package/src/public-cli/option-definitions.ts +14 -0
- package/src/public-cli/run-lifecycle.ts +10 -0
package/dist/public-cli/main.js
CHANGED
|
@@ -16645,7 +16645,8 @@ function buildActivationFlagArgs(activation) {
|
|
|
16645
16645
|
"--ak-collector-repo",
|
|
16646
16646
|
activation.repo,
|
|
16647
16647
|
...activation.pr === void 0 ? [] : ["--ak-collector-pr", activation.pr],
|
|
16648
|
-
...activation.requestManifestPath === void 0 ? [] : ["--ak-collector-request-manifest", activation.requestManifestPath]
|
|
16648
|
+
...activation.requestManifestPath === void 0 ? [] : ["--ak-collector-request-manifest", activation.requestManifestPath],
|
|
16649
|
+
...activation.waitMs === void 0 ? [] : ["--ak-collector-wait-ms", activation.waitMs]
|
|
16649
16650
|
];
|
|
16650
16651
|
case "doctor":
|
|
16651
16652
|
return ["--ak-role", "doctor", "--ak-doctor-case", activation.casePath];
|
|
@@ -18491,6 +18492,7 @@ async function loadResumableRunRecord(home, runId, authority) {
|
|
|
18491
18492
|
let repositoryDisplay;
|
|
18492
18493
|
let requestManifestPath;
|
|
18493
18494
|
let manifestDigest;
|
|
18495
|
+
let waitWindowMs;
|
|
18494
18496
|
let issueNumber;
|
|
18495
18497
|
let caseRunsPath;
|
|
18496
18498
|
let caseIdentity2;
|
|
@@ -18544,6 +18546,9 @@ async function loadResumableRunRecord(home, runId, authority) {
|
|
|
18544
18546
|
if (typeof record4.manifestDigest === "string" && record4.manifestDigest.trim() !== "") {
|
|
18545
18547
|
manifestDigest = record4.manifestDigest;
|
|
18546
18548
|
}
|
|
18549
|
+
if (typeof record4.waitWindowMs === "number" && Number.isSafeInteger(record4.waitWindowMs) && record4.waitWindowMs >= 1) {
|
|
18550
|
+
waitWindowMs = record4.waitWindowMs;
|
|
18551
|
+
}
|
|
18547
18552
|
if (typeof record4.issueNumber === "number" && Number.isSafeInteger(record4.issueNumber) && record4.issueNumber >= 1) {
|
|
18548
18553
|
issueNumber = record4.issueNumber;
|
|
18549
18554
|
}
|
|
@@ -18659,6 +18664,7 @@ async function loadResumableRunRecord(home, runId, authority) {
|
|
|
18659
18664
|
...repositoryDisplay === void 0 ? {} : { repositoryDisplay },
|
|
18660
18665
|
...requestManifestPath === void 0 ? {} : { requestManifestPath },
|
|
18661
18666
|
...manifestDigest === void 0 ? {} : { manifestDigest },
|
|
18667
|
+
...waitWindowMs === void 0 ? {} : { waitWindowMs },
|
|
18662
18668
|
...issueNumber === void 0 ? {} : { issueNumber },
|
|
18663
18669
|
...caseRunsPath === void 0 ? {} : { caseRunsPath },
|
|
18664
18670
|
...caseIdentity2 === void 0 ? {} : { caseIdentity: caseIdentity2 },
|
|
@@ -18916,6 +18922,7 @@ async function loadResumableCollectorRun(home, runId, authority) {
|
|
|
18916
18922
|
...prNumber === void 0 ? {} : { prNumber },
|
|
18917
18923
|
repository: parsedRepository,
|
|
18918
18924
|
...loaded.admittedFields.requestManifestPath === void 0 ? {} : { requestManifestPath: loaded.admittedFields.requestManifestPath },
|
|
18925
|
+
...loaded.admittedFields.waitWindowMs === void 0 ? {} : { waitWindowMs: loaded.admittedFields.waitWindowMs },
|
|
18919
18926
|
manifestDigest
|
|
18920
18927
|
};
|
|
18921
18928
|
return seatLoadedResult(loaded, admitted);
|
|
@@ -19744,6 +19751,20 @@ var init_option_definitions = __esm({
|
|
|
19744
19751
|
en: "Optional request manifest JSON path ({requests:[{id,body}]}). Role may also decide request bodies from handbook/field activity without this file.",
|
|
19745
19752
|
zh: "\u53EF\u9009 request manifest JSON \u8DEF\u5F84\uFF08{requests:[{id,body}]}\uFF09\u3002\u89D2\u8272\u4EA6\u53EF\u4F9D\u624B\u518C/\u73B0\u573A\u81EA\u884C\u5224\u5B9A\u8BF7\u6C42\u6B63\u6587\uFF0C\u4E0D\u5FC5\u4F9D\u8D56\u672C\u6587\u4EF6\u3002"
|
|
19746
19753
|
}
|
|
19754
|
+
},
|
|
19755
|
+
{
|
|
19756
|
+
id: "wait-ms",
|
|
19757
|
+
owner: "collector",
|
|
19758
|
+
canonical: "--wait-ms",
|
|
19759
|
+
aliases: [],
|
|
19760
|
+
valueMetavar: "ms",
|
|
19761
|
+
required: false,
|
|
19762
|
+
repeatable: false,
|
|
19763
|
+
form: "option",
|
|
19764
|
+
description: {
|
|
19765
|
+
en: "Wait-window duration in milliseconds after the work step opens (default 600000 = 10 minutes). Does not require a code change to adjust.",
|
|
19766
|
+
zh: "\u5DE5\u4F5C\u6B65\u9AA4\u5F00\u542F\u540E\u7684\u7B49\u5F85\u7A97\u65F6\u957F\uFF08\u6BEB\u79D2\uFF1B\u9ED8\u8BA4 600000\uFF1D\u5341\u5206\u949F\uFF09\u3002\u6539\u914D\u7F6E\u5373\u53EF\uFF0C\u65E0\u9700\u6539\u4EE3\u7801\u3002"
|
|
19767
|
+
}
|
|
19747
19768
|
}
|
|
19748
19769
|
];
|
|
19749
19770
|
DOCTOR_OPTIONS = [
|
|
@@ -21024,6 +21045,7 @@ function parseCollectorArgv(args) {
|
|
|
21024
21045
|
let repo;
|
|
21025
21046
|
let prNumber;
|
|
21026
21047
|
let requestManifestPath;
|
|
21048
|
+
let waitWindowMs;
|
|
21027
21049
|
const positional = [];
|
|
21028
21050
|
const tokens = [...args];
|
|
21029
21051
|
const definitions = roleOptions("collector");
|
|
@@ -21056,6 +21078,17 @@ function parseCollectorArgv(args) {
|
|
|
21056
21078
|
requestManifestPath = requireOptionPath(taken.def.canonical, taken.value);
|
|
21057
21079
|
continue;
|
|
21058
21080
|
}
|
|
21081
|
+
if (taken.def.id === "wait-ms") {
|
|
21082
|
+
if (taken.value === void 0 || !/^[1-9]\d*$/.test(taken.value.trim())) {
|
|
21083
|
+
throw new CliUsageError("--wait-ms requires a positive safe-integer millisecond value");
|
|
21084
|
+
}
|
|
21085
|
+
const parsed = Number(taken.value.trim());
|
|
21086
|
+
if (!Number.isSafeInteger(parsed) || parsed < 1) {
|
|
21087
|
+
throw new CliUsageError("--wait-ms requires a positive safe-integer millisecond value");
|
|
21088
|
+
}
|
|
21089
|
+
waitWindowMs = parsed;
|
|
21090
|
+
continue;
|
|
21091
|
+
}
|
|
21059
21092
|
throw new CliUsageError(`unknown collector option: ${taken.def.canonical}`);
|
|
21060
21093
|
}
|
|
21061
21094
|
const token = tokens.shift();
|
|
@@ -21071,7 +21104,8 @@ function parseCollectorArgv(args) {
|
|
|
21071
21104
|
attachmentPaths,
|
|
21072
21105
|
...project === void 0 ? {} : { project },
|
|
21073
21106
|
...repo === void 0 ? {} : { repo },
|
|
21074
|
-
...requestManifestPath === void 0 ? {} : { requestManifestPath }
|
|
21107
|
+
...requestManifestPath === void 0 ? {} : { requestManifestPath },
|
|
21108
|
+
...waitWindowMs === void 0 ? {} : { waitWindowMs }
|
|
21075
21109
|
};
|
|
21076
21110
|
}
|
|
21077
21111
|
function resolveGitHubRemoteRepository(projectRoot) {
|
|
@@ -21196,6 +21230,7 @@ async function admitCollectorInvocation(options) {
|
|
|
21196
21230
|
repository: repository.canonical,
|
|
21197
21231
|
repositoryDisplay: repository.display,
|
|
21198
21232
|
...requestManifestPath === void 0 ? {} : { requestManifestPath },
|
|
21233
|
+
...options.waitWindowMs === void 0 ? {} : { waitWindowMs: options.waitWindowMs },
|
|
21199
21234
|
manifestDigest,
|
|
21200
21235
|
attachments: attachments.map((a) => ({
|
|
21201
21236
|
provenancePath: a.provenancePath,
|
|
@@ -21225,6 +21260,7 @@ async function admitCollectorInvocation(options) {
|
|
|
21225
21260
|
...prNumber === void 0 ? {} : { prNumber },
|
|
21226
21261
|
repository,
|
|
21227
21262
|
...requestManifestPath === void 0 ? {} : { requestManifestPath },
|
|
21263
|
+
...options.waitWindowMs === void 0 ? {} : { waitWindowMs: options.waitWindowMs },
|
|
21228
21264
|
manifestDigest
|
|
21229
21265
|
};
|
|
21230
21266
|
}
|
|
@@ -23597,11 +23633,11 @@ var init_reviewer_dispatch_rejection = __esm({
|
|
|
23597
23633
|
});
|
|
23598
23634
|
|
|
23599
23635
|
// src/collector-evidence.ts
|
|
23600
|
-
var
|
|
23636
|
+
var COLLECTOR_DEFAULT_WAIT_WINDOW_MS;
|
|
23601
23637
|
var init_collector_evidence = __esm({
|
|
23602
23638
|
"src/collector-evidence.ts"() {
|
|
23603
23639
|
"use strict";
|
|
23604
|
-
|
|
23640
|
+
COLLECTOR_DEFAULT_WAIT_WINDOW_MS = 10 * 60 * 1e3;
|
|
23605
23641
|
}
|
|
23606
23642
|
});
|
|
23607
23643
|
|
|
@@ -27949,7 +27985,8 @@ function buildCollectorTurnRequest(admitted, options) {
|
|
|
27949
27985
|
repo: admitted.repository.display,
|
|
27950
27986
|
// #676 A: omit pr when unbound — role binds via ak_collector_bind_target.
|
|
27951
27987
|
...admitted.prNumber === void 0 ? {} : { pr: String(admitted.prNumber) },
|
|
27952
|
-
...admitted.requestManifestPath === void 0 ? {} : { requestManifestPath: admitted.requestManifestPath }
|
|
27988
|
+
...admitted.requestManifestPath === void 0 ? {} : { requestManifestPath: admitted.requestManifestPath },
|
|
27989
|
+
...admitted.waitWindowMs === void 0 ? {} : { waitMs: String(admitted.waitWindowMs) }
|
|
27953
27990
|
}
|
|
27954
27991
|
},
|
|
27955
27992
|
options
|
|
@@ -27969,6 +28006,7 @@ async function runPublicCollector(argv, env, io, parseCollectorArgv2) {
|
|
|
27969
28006
|
...parsed.project === void 0 ? {} : { project: parsed.project },
|
|
27970
28007
|
...parsed.repo === void 0 ? {} : { repo: parsed.repo },
|
|
27971
28008
|
...parsed.requestManifestPath === void 0 ? {} : { requestManifestPath: parsed.requestManifestPath },
|
|
28009
|
+
...parsed.waitWindowMs === void 0 ? {} : { waitWindowMs: parsed.waitWindowMs },
|
|
27972
28010
|
...env.createRunId === void 0 ? {} : { createRunId: env.createRunId },
|
|
27973
28011
|
...env.model === void 0 ? {} : { model: env.model }
|
|
27974
28012
|
});
|
|
@@ -487,6 +487,20 @@ const COLLECTOR_OPTIONS = [
|
|
|
487
487
|
zh: "可选 request manifest JSON 路径({requests:[{id,body}]})。角色亦可依手册/现场自行判定请求正文,不必依赖本文件。",
|
|
488
488
|
},
|
|
489
489
|
},
|
|
490
|
+
{
|
|
491
|
+
id: "wait-ms",
|
|
492
|
+
owner: "collector",
|
|
493
|
+
canonical: "--wait-ms",
|
|
494
|
+
aliases: [],
|
|
495
|
+
valueMetavar: "ms",
|
|
496
|
+
required: false,
|
|
497
|
+
repeatable: false,
|
|
498
|
+
form: "option",
|
|
499
|
+
description: {
|
|
500
|
+
en: "Wait-window duration in milliseconds after the work step opens (default 600000 = 10 minutes). Does not require a code change to adjust.",
|
|
501
|
+
zh: "工作步骤开启后的等待窗时长(毫秒;默认 600000=十分钟)。改配置即可,无需改代码。",
|
|
502
|
+
},
|
|
503
|
+
},
|
|
490
504
|
];
|
|
491
505
|
const DOCTOR_OPTIONS = [
|
|
492
506
|
bindOwner("doctor", SHARED_PROJECT_SEMANTICS),
|
|
@@ -880,6 +880,7 @@ async function loadResumableRunRecord(home, runId, authority) {
|
|
|
880
880
|
let repositoryDisplay;
|
|
881
881
|
let requestManifestPath;
|
|
882
882
|
let manifestDigest;
|
|
883
|
+
let waitWindowMs;
|
|
883
884
|
let issueNumber;
|
|
884
885
|
let caseRunsPath;
|
|
885
886
|
let caseIdentity;
|
|
@@ -934,6 +935,9 @@ async function loadResumableRunRecord(home, runId, authority) {
|
|
|
934
935
|
if (typeof record.manifestDigest === "string" && record.manifestDigest.trim() !== "") {
|
|
935
936
|
manifestDigest = record.manifestDigest;
|
|
936
937
|
}
|
|
938
|
+
if (typeof record.waitWindowMs === "number" && Number.isSafeInteger(record.waitWindowMs) && record.waitWindowMs >= 1) {
|
|
939
|
+
waitWindowMs = record.waitWindowMs;
|
|
940
|
+
}
|
|
937
941
|
// Doctor — admitted single-case identity (#633 resume).
|
|
938
942
|
if (typeof record.issueNumber === "number" && Number.isSafeInteger(record.issueNumber) && record.issueNumber >= 1) {
|
|
939
943
|
issueNumber = record.issueNumber;
|
|
@@ -1074,6 +1078,7 @@ async function loadResumableRunRecord(home, runId, authority) {
|
|
|
1074
1078
|
...(repositoryDisplay === undefined ? {} : { repositoryDisplay }),
|
|
1075
1079
|
...(requestManifestPath === undefined ? {} : { requestManifestPath }),
|
|
1076
1080
|
...(manifestDigest === undefined ? {} : { manifestDigest }),
|
|
1081
|
+
...(waitWindowMs === undefined ? {} : { waitWindowMs }),
|
|
1077
1082
|
...(issueNumber === undefined ? {} : { issueNumber }),
|
|
1078
1083
|
...(caseRunsPath === undefined ? {} : { caseRunsPath }),
|
|
1079
1084
|
...(caseIdentity === undefined ? {} : { caseIdentity }),
|
|
@@ -1338,6 +1343,9 @@ export async function loadResumableCollectorRun(home, runId, authority) {
|
|
|
1338
1343
|
...(loaded.admittedFields.requestManifestPath === undefined
|
|
1339
1344
|
? {}
|
|
1340
1345
|
: { requestManifestPath: loaded.admittedFields.requestManifestPath }),
|
|
1346
|
+
...(loaded.admittedFields.waitWindowMs === undefined
|
|
1347
|
+
? {}
|
|
1348
|
+
: { waitWindowMs: loaded.admittedFields.waitWindowMs }),
|
|
1341
1349
|
manifestDigest,
|
|
1342
1350
|
};
|
|
1343
1351
|
return seatLoadedResult(loaded, admitted);
|
package/package.json
CHANGED
package/souls/countersign.md
CHANGED
|
@@ -8,37 +8,39 @@
|
|
|
8
8
|
**一、立法。** 先为本票立「适用法」;后面每一步对着它问,未立法不得往下走。适用法三部分:
|
|
9
9
|
1 第一法源立法(最高法):该票起居录,全局宪法,仓内治理文件;
|
|
10
10
|
|
|
11
|
-
2
|
|
11
|
+
2 第二法源立法:
|
|
12
12
|
适用 ADR——由你自行检索,按票面触及的席位、接缝、术语在 docs/adr 与 CONTEXT.md 查全,票面引没引都算——并逐键列出其决策键;质量法、审刑院法典;
|
|
13
13
|
|
|
14
|
-
3 第二法源合法性检查:判断第二法源 以及 输入的 prompt等
|
|
14
|
+
3 第二法源合法性检查:判断第二法源 以及 输入的 prompt等 对比第一法源是否有超出,不足,曲解。如有,除非有陛下原话批准,否则直接封驳 要求改正或陛下批准。
|
|
15
15
|
|
|
16
|
-
|
|
16
|
+
**二、相抵** 票面,相关票与在飞分支与法源有无抵触。
|
|
17
17
|
|
|
18
|
-
|
|
18
|
+
**三、缺失** 法源要求的,票面有没有漏:适用 ADR 未被引用即缺失,引用须写 `docs/adr/...md` 路径;ADR 每个决策键须有落点或明写「由 #x 承接」;验收面须覆盖票面每条主张;前置产物、决定、答复须在;承重主张的源头不存在、不支持、已过时,同为缺失。缺失列表在能力范围内一次扫完,尽量避免因缺失导致多轮往返。
|
|
19
19
|
|
|
20
|
-
|
|
20
|
+
**四、擅加** 票面有没有超出法源的机制、约束、验收、术语。
|
|
21
|
+
|
|
22
|
+
**五、符合陛下意图** 先根据起居录对话,判断陛下意图。再证实票面是否是陛下想要的,对于修复票,判断 根因是什么、证据在哪 。对于新功能设计票,判断设计是不是合理,是不是陛下想要的方向。以及 因果链本身站不站得住
|
|
21
23
|
|
|
22
24
|
各步取证按《审法·取证边界》:读码、开卷、传召皆可;探针只落 worktree 与临时目录,不得删改其外任何路径。交付时候清理回原样
|
|
23
25
|
|
|
24
26
|
## 审读五问
|
|
25
27
|
|
|
26
|
-
|
|
28
|
+
五步是审读,五问是裁决口径,对应关系固定,不重做:
|
|
27
29
|
1. 票面是否符合既定制度——由「相抵」答;
|
|
28
30
|
2. 授权是否真实——由「立法②」答:不认转述与口头;
|
|
29
|
-
3.
|
|
31
|
+
3. 文书是否与原意一致——由「缺失」「擅加」「符合陛下意图」答:无过度解释、无夹带、无缺口、符合原意;
|
|
30
32
|
4. 是否存在必须退回重议的问题;
|
|
31
33
|
5. 是否已具备正式发布与执行资格。
|
|
32
34
|
|
|
33
|
-
|
|
35
|
+
**每次开庭都是全票审读,复审亦然——五步五问对全票重新过一遍;上轮封驳条目只是必须核销的部分,不划定审读范围。永远全审,没有局部一说。**
|
|
34
36
|
|
|
35
37
|
## 判词
|
|
36
38
|
|
|
37
39
|
事实与处方分开对宪:裁决落法度与事实;施工设计与实现细节过早堆上票面即为失职。
|
|
38
40
|
|
|
39
|
-
- 署(converged
|
|
41
|
+
- 署(converged)——五步无碍、五问皆有据可答,具备开工资格;
|
|
40
42
|
- 封驳(continue)——存在必须退回重议的问题;写明退给谁、缺什么、属哪一步;
|
|
41
|
-
- 上呈(escalate
|
|
43
|
+
- 上呈(escalate)——无法正常署/封驳时。上呈求助
|
|
42
44
|
|
|
43
45
|
|
|
44
46
|
主张必须附证据或法的具体条目。finding 提类别,同类一次说全(审刑院法典·审官纪律)。
|
|
@@ -98,6 +98,7 @@ export function projectAcpActivationFlags(request: RoleTurnRequest): Map<string,
|
|
|
98
98
|
// #676 D1: pr optional at admission; omit flag when role binds from materials.
|
|
99
99
|
if (activation.pr !== undefined) flags.set("ak-collector-pr", activation.pr);
|
|
100
100
|
if (activation.requestManifestPath !== undefined) flags.set("ak-collector-request-manifest", activation.requestManifestPath);
|
|
101
|
+
if (activation.waitMs !== undefined) flags.set("ak-collector-wait-ms", activation.waitMs);
|
|
101
102
|
}
|
|
102
103
|
return flags;
|
|
103
104
|
}
|
|
@@ -11,7 +11,8 @@ import type {
|
|
|
11
11
|
GitHubUser,
|
|
12
12
|
} from "./collector-github.ts";
|
|
13
13
|
|
|
14
|
-
|
|
14
|
+
/** Default collector wait window (D4): 10 minutes from the work-step open, not from session activation. */
|
|
15
|
+
export const COLLECTOR_DEFAULT_WAIT_WINDOW_MS = 10 * 60 * 1000;
|
|
15
16
|
|
|
16
17
|
export type WindowRelation = "before" | "within" | "after" | "uncertain";
|
|
17
18
|
export type HeadRelation = "current" | "prior";
|
|
@@ -73,6 +74,8 @@ export type CollectorSnapshot = {
|
|
|
73
74
|
prNumber: number;
|
|
74
75
|
prState: string;
|
|
75
76
|
headOid: string;
|
|
77
|
+
/** PR create success time when GitHub supplies it (#678 new-PR wait start). */
|
|
78
|
+
prCreatedAt?: string;
|
|
76
79
|
complete: boolean;
|
|
77
80
|
evidenceIds: string[];
|
|
78
81
|
pageDiagnostics: GitHubPageDiagnostics[];
|
|
@@ -177,6 +180,7 @@ export function normalizePullRequestEvidence(
|
|
|
177
180
|
number: pr.number,
|
|
178
181
|
state: pr.state,
|
|
179
182
|
headOid: pr.headOid,
|
|
183
|
+
createdAt: pr.createdAt ?? null,
|
|
180
184
|
updatedAt: pr.updatedAt ?? null,
|
|
181
185
|
htmlUrl: pr.url,
|
|
182
186
|
});
|
|
@@ -190,6 +194,7 @@ export function normalizePullRequestEvidence(
|
|
|
190
194
|
state: pr.state,
|
|
191
195
|
commitOid: pr.headOid,
|
|
192
196
|
htmlUrl: pr.url,
|
|
197
|
+
// Version clock stays updatedAt. Create success for the wait window is snapshot.prCreatedAt only (#678).
|
|
193
198
|
authoritativeTime: pr.updatedAt ?? null,
|
|
194
199
|
firstObservedAt: observedAt,
|
|
195
200
|
raw: pr.raw,
|
package/src/collector-github.ts
CHANGED
|
@@ -7,6 +7,8 @@ export type GitHubPullRequest = {
|
|
|
7
7
|
number: number;
|
|
8
8
|
state: string;
|
|
9
9
|
headOid: string;
|
|
10
|
+
/** GitHub PR created_at — work-step start for new-PR auto-trigger rounds (#678 D4). */
|
|
11
|
+
createdAt?: string;
|
|
10
12
|
updatedAt?: string;
|
|
11
13
|
url: string;
|
|
12
14
|
raw: unknown;
|
|
@@ -491,6 +493,7 @@ export function normalizePullRequest(raw: unknown): GitHubPullRequest {
|
|
|
491
493
|
number,
|
|
492
494
|
state,
|
|
493
495
|
headOid: head["sha"],
|
|
496
|
+
...(typeof raw["created_at"] === "string" ? { createdAt: raw["created_at"] } : {}),
|
|
494
497
|
...(typeof raw["updated_at"] === "string" ? { updatedAt: raw["updated_at"] } : {}),
|
|
495
498
|
url: htmlUrl,
|
|
496
499
|
raw,
|
|
@@ -151,6 +151,20 @@ export class CollectorNonOpenRequestError extends CorrectableSubmissionError {
|
|
|
151
151
|
}
|
|
152
152
|
}
|
|
153
153
|
|
|
154
|
+
/** #678: wait window not open or already ended — bounce without latching fatal so materials still seal. */
|
|
155
|
+
export class CollectorWaitWindowClosedError extends CorrectableSubmissionError {
|
|
156
|
+
constructor(action: "request" | "wait" | "wait-before-open") {
|
|
157
|
+
super(
|
|
158
|
+
action === "request"
|
|
159
|
+
? "通进司请求不在资格截止前"
|
|
160
|
+
: action === "wait-before-open"
|
|
161
|
+
? "通进司等待需要先在工作步骤开启等待窗(新建 PR 用创建成功时刻;已有 PR 在触发阶段结束后开启)"
|
|
162
|
+
: "通进司等待不在资格截止前",
|
|
163
|
+
);
|
|
164
|
+
this.name = "CollectorWaitWindowClosedError";
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
154
168
|
function candidateRecord(candidate: unknown): Record<string, unknown> | undefined {
|
|
155
169
|
if (candidate === undefined || candidate === null || typeof candidate !== "object" || Array.isArray(candidate)) {
|
|
156
170
|
return undefined;
|
package/src/collector-ledger.ts
CHANGED
|
@@ -2,7 +2,7 @@ import type { CollectorManifest, CollectorRepository } from "./collector-config.
|
|
|
2
2
|
import {
|
|
3
3
|
applyEvidenceVersionHistory,
|
|
4
4
|
assignWindowRelations,
|
|
5
|
-
|
|
5
|
+
COLLECTOR_DEFAULT_WAIT_WINDOW_MS,
|
|
6
6
|
measureNormalizedBytes,
|
|
7
7
|
normalizeAuthenticatedUserEvidence,
|
|
8
8
|
normalizeIssueCommentEvidence,
|
|
@@ -23,13 +23,18 @@ import {
|
|
|
23
23
|
type GitHubPageDiagnostics,
|
|
24
24
|
type GitHubPullRequest,
|
|
25
25
|
} from "./collector-github.ts";
|
|
26
|
-
import {
|
|
26
|
+
import {
|
|
27
|
+
CollectorNonOpenRequestError,
|
|
28
|
+
CollectorWaitWindowClosedError,
|
|
29
|
+
} from "./collector-identity.ts";
|
|
27
30
|
import { COLLECTOR_OUTPUT_TOOL } from "./package-contracts/collector-output.ts";
|
|
28
31
|
|
|
29
32
|
export const COLLECTOR_OBSERVE_TOOL = "ak_collector_observe";
|
|
30
33
|
export const COLLECTOR_READ_TOOL = "ak_collector_read";
|
|
31
34
|
export const COLLECTOR_REQUEST_TOOL = "ak_collector_request";
|
|
32
35
|
export const COLLECTOR_WAIT_TOOL = "ak_collector_wait";
|
|
36
|
+
/** #678 D4: open wait window at a work step — business tool, ledger-booked. */
|
|
37
|
+
export const COLLECTOR_OPEN_WAIT_WINDOW_TOOL = "ak_collector_open_wait_window";
|
|
33
38
|
/** #676 A: role-decided target bind — business tool, ledger-booked. */
|
|
34
39
|
export const COLLECTOR_BIND_TARGET_TOOL = "ak_collector_bind_target";
|
|
35
40
|
/** #677: opaque handbook write — business tool, ledger-booked. */
|
|
@@ -41,6 +46,7 @@ export const COLLECTOR_OPERATIONAL_TOOLS = [
|
|
|
41
46
|
COLLECTOR_OBSERVE_TOOL,
|
|
42
47
|
COLLECTOR_READ_TOOL,
|
|
43
48
|
COLLECTOR_REQUEST_TOOL,
|
|
49
|
+
COLLECTOR_OPEN_WAIT_WINDOW_TOOL,
|
|
44
50
|
COLLECTOR_WAIT_TOOL,
|
|
45
51
|
COLLECTOR_HANDBOOK_WRITE_TOOL,
|
|
46
52
|
] as const;
|
|
@@ -83,6 +89,8 @@ export type ObserveModelView = {
|
|
|
83
89
|
completedAt: string;
|
|
84
90
|
prState: string;
|
|
85
91
|
headOid: string;
|
|
92
|
+
/** PR create success time when known — new-PR wait-window startedAt (#678 D4). */
|
|
93
|
+
prCreatedAt?: string;
|
|
86
94
|
complete: boolean;
|
|
87
95
|
evidence: ObserveEvidenceEntry[];
|
|
88
96
|
requestAttempts: Array<{
|
|
@@ -158,6 +166,15 @@ export type CollectorConfigState = {
|
|
|
158
166
|
/** Bound PR target; undefined until explicit flag, admission bind, or role bind-target tool. */
|
|
159
167
|
prNumber: number | undefined;
|
|
160
168
|
manifest: CollectorManifest;
|
|
169
|
+
/**
|
|
170
|
+
* Wait window duration (#678 D4). Default 10 minutes; caller-configurable without code change.
|
|
171
|
+
* Window opens at a work step (PR create success / trigger-phase end), not session activation.
|
|
172
|
+
*/
|
|
173
|
+
waitWindowMs: number;
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
export type CollectorLedgerConfigInput = Omit<CollectorConfigState, "waitWindowMs"> & {
|
|
177
|
+
waitWindowMs?: number;
|
|
161
178
|
};
|
|
162
179
|
|
|
163
180
|
/** Durable session journal sink owned by the shared execution seam. */
|
|
@@ -177,8 +194,11 @@ export type CollectorLedger = {
|
|
|
177
194
|
readonly fatal: boolean;
|
|
178
195
|
readonly fatalReason: string | undefined;
|
|
179
196
|
readonly outputCandidate: boolean;
|
|
197
|
+
/** Session ready (before_agent_start); not the wait window. */
|
|
180
198
|
readonly activationRecorded: boolean;
|
|
199
|
+
/** Wait-window start (receipt activationTime); undefined until openWaitWindow. */
|
|
181
200
|
readonly activationTime: Date | undefined;
|
|
201
|
+
/** Wait-window end; undefined until openWaitWindow. */
|
|
182
202
|
readonly deadlineTime: Date | undefined;
|
|
183
203
|
readonly activationMono: number | undefined;
|
|
184
204
|
readonly deadlineMono: number | undefined;
|
|
@@ -192,7 +212,13 @@ export type CollectorLedger = {
|
|
|
192
212
|
|
|
193
213
|
latchFatal(reason: string, cause?: unknown): Error;
|
|
194
214
|
assertNotFatal(): void;
|
|
215
|
+
/** Mark session ready for prep observe/request; does not open the wait window (#678). */
|
|
195
216
|
recordActivation(clock: CollectorClock): void;
|
|
217
|
+
/**
|
|
218
|
+
* Open the wait window once at a work step. Optional startedAt covers new-PR create success;
|
|
219
|
+
* omit for existing-PR trigger-phase end (= now). Second call is a no-op (D6: updates do not reset).
|
|
220
|
+
*/
|
|
221
|
+
openWaitWindow(clock: CollectorClock, options?: { startedAt?: Date }): void;
|
|
196
222
|
recordOutputCandidate(): void;
|
|
197
223
|
/** #676 A: role-decided unique PR bind (business fact on the ledger). */
|
|
198
224
|
bindTarget(prNumber: number): void;
|
|
@@ -237,16 +263,31 @@ function isOperationalTool(name: string): name is CollectorOperationalTool {
|
|
|
237
263
|
return (COLLECTOR_OPERATIONAL_TOOLS as readonly string[]).includes(name);
|
|
238
264
|
}
|
|
239
265
|
|
|
266
|
+
function resolveWaitWindowMs(raw: number | undefined): number {
|
|
267
|
+
if (raw === undefined) return COLLECTOR_DEFAULT_WAIT_WINDOW_MS;
|
|
268
|
+
if (!Number.isSafeInteger(raw) || raw < 1) {
|
|
269
|
+
throw new Error("通进司 waitWindowMs 须为正安全整数毫秒");
|
|
270
|
+
}
|
|
271
|
+
return raw;
|
|
272
|
+
}
|
|
273
|
+
|
|
240
274
|
export function createCollectorLedger(
|
|
241
|
-
|
|
275
|
+
input: CollectorLedgerConfigInput,
|
|
242
276
|
options?: CollectorLedgerOptions,
|
|
243
277
|
): CollectorLedger {
|
|
278
|
+
const config: CollectorConfigState = {
|
|
279
|
+
repository: input.repository,
|
|
280
|
+
prNumber: input.prNumber,
|
|
281
|
+
manifest: input.manifest,
|
|
282
|
+
waitWindowMs: resolveWaitWindowMs(input.waitWindowMs),
|
|
283
|
+
};
|
|
244
284
|
const clock = options?.clock;
|
|
245
285
|
const journal = options?.journal;
|
|
246
286
|
let fatal = false;
|
|
247
287
|
let fatalReason: string | undefined;
|
|
248
288
|
let outputCandidate = false;
|
|
249
289
|
let pendingOutputCallId: string | undefined;
|
|
290
|
+
let sessionReady = false;
|
|
250
291
|
let activationTime: Date | undefined;
|
|
251
292
|
let deadlineTime: Date | undefined;
|
|
252
293
|
let activationMono: number | undefined;
|
|
@@ -297,7 +338,7 @@ export function createCollectorLedger(
|
|
|
297
338
|
};
|
|
298
339
|
|
|
299
340
|
const remainingMs = (clock: CollectorClock): number => {
|
|
300
|
-
if (deadlineMono === undefined) return
|
|
341
|
+
if (deadlineMono === undefined) return config.waitWindowMs;
|
|
301
342
|
return Math.max(0, deadlineMono - monoNowOrThrow(clock));
|
|
302
343
|
};
|
|
303
344
|
|
|
@@ -380,7 +421,9 @@ export function createCollectorLedger(
|
|
|
380
421
|
};
|
|
381
422
|
|
|
382
423
|
const commitActivationWindow = (nextActivation: Date, nextDeadline: Date): void => {
|
|
424
|
+
// D6: once open, PR updates / second open must not reset the window.
|
|
383
425
|
if (activationTime !== undefined) return;
|
|
426
|
+
sessionReady = true;
|
|
384
427
|
activationTime = nextActivation;
|
|
385
428
|
deadlineTime = nextDeadline;
|
|
386
429
|
bindDeadlineMonoFromWall();
|
|
@@ -520,6 +563,8 @@ export function createCollectorLedger(
|
|
|
520
563
|
if (entry.customType === COLLECTOR_ACTIVATION_ENTRY_TYPE) {
|
|
521
564
|
if (typeof data.activationTime === "string" && typeof data.deadlineTime === "string") {
|
|
522
565
|
commitActivationWindow(new Date(data.activationTime), new Date(data.deadlineTime));
|
|
566
|
+
} else if (data.sessionReady === true) {
|
|
567
|
+
sessionReady = true;
|
|
523
568
|
}
|
|
524
569
|
continue;
|
|
525
570
|
}
|
|
@@ -581,7 +626,7 @@ export function createCollectorLedger(
|
|
|
581
626
|
return outputCandidate || pendingOutputCallId !== undefined;
|
|
582
627
|
},
|
|
583
628
|
get activationRecorded() {
|
|
584
|
-
return
|
|
629
|
+
return sessionReady;
|
|
585
630
|
},
|
|
586
631
|
get activationTime() {
|
|
587
632
|
return activationTime;
|
|
@@ -620,16 +665,38 @@ export function createCollectorLedger(
|
|
|
620
665
|
latchFatal,
|
|
621
666
|
assertNotFatal,
|
|
622
667
|
|
|
623
|
-
recordActivation(
|
|
668
|
+
recordActivation(_clock) {
|
|
669
|
+
assertNotFatal();
|
|
670
|
+
if (sessionReady) return;
|
|
671
|
+
sessionReady = true;
|
|
672
|
+
// Session ready only — wait window opens later at a work step (#678 D4).
|
|
673
|
+
appendJournal(COLLECTOR_ACTIVATION_ENTRY_TYPE, {
|
|
674
|
+
sessionReady: true,
|
|
675
|
+
});
|
|
676
|
+
},
|
|
677
|
+
|
|
678
|
+
openWaitWindow(clock, options) {
|
|
624
679
|
assertNotFatal();
|
|
625
|
-
if (
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
680
|
+
if (!sessionReady) {
|
|
681
|
+
throw latchFatal("通进司开启等待窗需要先激活会话");
|
|
682
|
+
}
|
|
683
|
+
if (activationTime !== undefined) return; // D6: do not reset
|
|
684
|
+
const startedAt = options?.startedAt ?? clock.wallNow();
|
|
685
|
+
if (!(startedAt instanceof Date) || Number.isNaN(startedAt.getTime())) {
|
|
686
|
+
throw new Error("通进司等待窗 startedAt 须为有效时间");
|
|
687
|
+
}
|
|
688
|
+
const nextDeadline = new Date(startedAt.getTime() + config.waitWindowMs);
|
|
689
|
+
// Bind mono relative to current clock so a past startedAt shortens remaining correctly.
|
|
690
|
+
activationTime = startedAt;
|
|
691
|
+
deadlineTime = nextDeadline;
|
|
692
|
+
const wallNow = clock.wallNow().getTime();
|
|
693
|
+
const monoNow = clock.monoNow();
|
|
694
|
+
activationMono = monoNow - (wallNow - startedAt.getTime());
|
|
695
|
+
deadlineMono = monoNow + (nextDeadline.getTime() - wallNow);
|
|
630
696
|
appendJournal(COLLECTOR_ACTIVATION_ENTRY_TYPE, {
|
|
631
697
|
activationTime: activationTime.toISOString(),
|
|
632
698
|
deadlineTime: deadlineTime.toISOString(),
|
|
699
|
+
waitWindowMs: config.waitWindowMs,
|
|
633
700
|
});
|
|
634
701
|
},
|
|
635
702
|
|
|
@@ -733,7 +800,7 @@ export function createCollectorLedger(
|
|
|
733
800
|
|
|
734
801
|
async observe(transport, clock, signal) {
|
|
735
802
|
assertNotFatal();
|
|
736
|
-
if (
|
|
803
|
+
if (!sessionReady) {
|
|
737
804
|
throw latchFatal("通进司观察需要激活");
|
|
738
805
|
}
|
|
739
806
|
if (signal?.aborted) {
|
|
@@ -841,6 +908,7 @@ export function createCollectorLedger(
|
|
|
841
908
|
prNumber: requireBoundPr(),
|
|
842
909
|
prState: pr.state,
|
|
843
910
|
headOid: pr.headOid,
|
|
911
|
+
...(pr.createdAt === undefined ? {} : { prCreatedAt: pr.createdAt }),
|
|
844
912
|
complete: true,
|
|
845
913
|
evidenceIds: storedIds,
|
|
846
914
|
pageDiagnostics,
|
|
@@ -877,7 +945,7 @@ export function createCollectorLedger(
|
|
|
877
945
|
|
|
878
946
|
async request(input, transport, clock, signal) {
|
|
879
947
|
assertNotFatal();
|
|
880
|
-
if (
|
|
948
|
+
if (!sessionReady) {
|
|
881
949
|
throw latchFatal("通进司请求需要激活");
|
|
882
950
|
}
|
|
883
951
|
if (ledger.unresolvedTransportFailure) {
|
|
@@ -918,7 +986,8 @@ export function createCollectorLedger(
|
|
|
918
986
|
}
|
|
919
987
|
if (pastCutoff(clock)) {
|
|
920
988
|
finalObservationRequired = true;
|
|
921
|
-
|
|
989
|
+
// #678: do not latch fatal — materials must still seal after the window ends.
|
|
990
|
+
throw new CollectorWaitWindowClosedError("request");
|
|
922
991
|
}
|
|
923
992
|
|
|
924
993
|
// Caller manifest body wins when requestId is configured; otherwise role body.
|
|
@@ -1035,23 +1104,23 @@ export function createCollectorLedger(
|
|
|
1035
1104
|
|
|
1036
1105
|
async wait(input, clock, signal) {
|
|
1037
1106
|
assertNotFatal();
|
|
1038
|
-
if (
|
|
1107
|
+
if (!sessionReady) {
|
|
1039
1108
|
throw latchFatal("通进司等待需要激活");
|
|
1040
1109
|
}
|
|
1110
|
+
// #678: wait never invents a window start. Work-step open (create-success / trigger-end)
|
|
1111
|
+
// must be explicit — silent "now" would break the new-PR create-success clock.
|
|
1112
|
+
if (activationTime === undefined || deadlineTime === undefined) {
|
|
1113
|
+
throw new CollectorWaitWindowClosedError("wait-before-open");
|
|
1114
|
+
}
|
|
1041
1115
|
// durationMs shape authority = collectorWaitArgsSchema (host parameters).
|
|
1042
1116
|
if (pastCutoff(clock)) {
|
|
1043
1117
|
finalObservationRequired = true;
|
|
1044
|
-
throw
|
|
1118
|
+
throw new CollectorWaitWindowClosedError("wait");
|
|
1045
1119
|
}
|
|
1046
1120
|
|
|
1047
1121
|
const remaining = remainingMs(clock);
|
|
1048
|
-
//
|
|
1049
|
-
const
|
|
1050
|
-
const effectiveMs = Math.min(
|
|
1051
|
-
input.durationMs,
|
|
1052
|
-
remaining,
|
|
1053
|
-
COLLECTOR_SINGLE_WAIT_MAX_MS,
|
|
1054
|
-
);
|
|
1122
|
+
// Cap only by remaining window — no package-local single-wait ceiling (ADR 0035 / #678).
|
|
1123
|
+
const effectiveMs = Math.min(input.durationMs, remaining);
|
|
1055
1124
|
const startedAt = clock.wallNow().toISOString();
|
|
1056
1125
|
const waitId = sha256Text(`wait:${startedAt}:${effectiveMs}`).slice(0, 16);
|
|
1057
1126
|
await clock.sleep(effectiveMs, signal);
|
|
@@ -1143,6 +1212,9 @@ function buildObserveModelView(input: {
|
|
|
1143
1212
|
completedAt: input.snapshot.completedAt,
|
|
1144
1213
|
prState: input.snapshot.prState,
|
|
1145
1214
|
headOid: input.snapshot.headOid,
|
|
1215
|
+
...(input.snapshot.prCreatedAt === undefined
|
|
1216
|
+
? {}
|
|
1217
|
+
: { prCreatedAt: input.snapshot.prCreatedAt }),
|
|
1146
1218
|
complete: input.snapshot.complete,
|
|
1147
1219
|
evidence: relevant.map((record): ObserveEvidenceEntry => projectEvidenceEntryView(record)),
|
|
1148
1220
|
requestAttempts: input.attempts.map((attempt) => ({
|