@akagilnc/pi-workflow-roles 0.1.6014 → 0.1.6021
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 +2 -0
- package/README.zh-CN.md +2 -0
- package/dist/acp-host/production-host.js +172 -249
- package/dist/headless-host/production-host.js +181 -258
- package/dist/public-cli/instruction-seat-run.js +9 -146
- package/dist/public-cli/main.js +125 -213
- package/dist/public-cli/post-admission.js +1 -19
- package/dist/report-ticket-tool.js +1 -4
- package/dist/submission-ledger.js +16 -1
- package/package.json +1 -1
- package/resources/ticket-at-receipt.md +1 -0
- package/souls/audit-law.md +2 -0
- package/souls/judge.md +1 -1
- package/souls/quality-law.md +1 -1
- package/src/public-cli/instruction-seat-run.ts +9 -182
- package/src/public-cli/post-admission.ts +1 -37
- package/src/report-ticket-tool.ts +1 -5
- package/src/submission-ledger.ts +16 -1
- package/resources/missing-ticket-reask.md +0 -1
|
@@ -684,31 +684,13 @@ export async function dispatchPostAdmissionTurn(input) {
|
|
|
684
684
|
pendingTerminal !== undefined &&
|
|
685
685
|
result.terminal === pendingTerminal) {
|
|
686
686
|
try {
|
|
687
|
-
|
|
688
|
-
// preserves the sealed terminal face. ticketReasksSpent is budget across
|
|
689
|
-
// rounds — after audit continue, ordinary no-volume turns publish
|
|
690
|
-
// no_receipt honestly. previewOnly skips the terminal pointer;
|
|
691
|
-
// recordAttemptHistory still appends.
|
|
692
|
-
const softTicketReaskNoSeal = pendingSettlement !== "sealed" && env.softTicketReaskTurn === true;
|
|
693
|
-
const noSealScope = softTicketReaskNoSeal
|
|
694
|
-
? {
|
|
695
|
-
...courtScope,
|
|
696
|
-
previewOnly: true,
|
|
697
|
-
recordAttemptHistory: true,
|
|
698
|
-
}
|
|
699
|
-
: { ...courtScope, recordAttemptHistory: true };
|
|
687
|
+
const noSealScope = { ...courtScope, recordAttemptHistory: true };
|
|
700
688
|
const terminal = pendingSettlement === "sealed"
|
|
701
689
|
? await adapters.trySettle(admitted, env.principalAuthority, { ...courtScope, recordAttemptHistory: true })
|
|
702
690
|
: await attachRecordedSubmissions(admitted, await settleHostEndedNoReceipt(admitted, env.principalAuthority, noSealScope, receiptDelivery.issuedDeliveryRequests()), noSealScope);
|
|
703
691
|
if (terminal === undefined)
|
|
704
692
|
throw new Error("settled host attempt vanished before publication");
|
|
705
693
|
result = { ...result, terminal, exitCode: exitCodeForTerminalOutcome(terminal.roleOutcome) };
|
|
706
|
-
// #1171 F2-R7: ownership evidence at this soft-reask turn's seal —
|
|
707
|
-
// mutate the live ownership cell carried on env (survives spreads).
|
|
708
|
-
if (pendingSettlement === "sealed" && env.softTicketReaskTurn === true
|
|
709
|
-
&& env.softTicketReaskOwnership !== undefined) {
|
|
710
|
-
env.softTicketReaskOwnership.sealedSubstitute = true;
|
|
711
|
-
}
|
|
712
694
|
if (pendingSettlement === "sealed" && terminal.roleOutcome.kind === "accepted"
|
|
713
695
|
&& request.courtAttemptId !== undefined && request.courtAttemptId.length > 0) {
|
|
714
696
|
try {
|
|
@@ -20,8 +20,7 @@ const reportTicketArgsSchema = Type.Object({
|
|
|
20
20
|
}),
|
|
21
21
|
}, { additionalProperties: true });
|
|
22
22
|
export const REPORT_TICKET_TOOL_DESCRIPTION = "向包申报本票号。已知票号时开工第一件事调用;包当场把本腿归到票号目录。"
|
|
23
|
-
+ "尚无票号(例如中书省建票前)不要调用,取得后再报。"
|
|
24
|
-
+ "交卷时若仍未报过且交卷亦未带票号,包会经既有重交通道再问一次。";
|
|
23
|
+
+ "尚无票号(例如中书省建票前)不要调用,取得后再报。";
|
|
25
24
|
function pageString(page, key) {
|
|
26
25
|
const value = page?.[key];
|
|
27
26
|
return typeof value === "string" && value.trim() !== "" ? value : undefined;
|
|
@@ -153,5 +152,3 @@ export function registerReportTicketTool(roleHost, hostActions) {
|
|
|
153
152
|
},
|
|
154
153
|
});
|
|
155
154
|
}
|
|
156
|
-
/** Soft re-ask when a sealed submission still leaves the leg unbound (#1171 / ADR 0073). */
|
|
157
|
-
export const MISSING_TICKET_REASK_MATERIAL = "resources/missing-ticket-reask.md";
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { sessionFileIn, sessionFileOf } from "./role-run-placement.js";
|
|
1
|
+
import { isUnboundRunDirectory, sessionFileIn, sessionFileOf } from "./role-run-placement.js";
|
|
2
|
+
import { readPackageMaterial } from "./session-opening-materials.js";
|
|
2
3
|
import { resolveActivationLedgerHome, tryHomeFromAkRolesPath, } from "./activation-ledger-topology.js";
|
|
3
4
|
import { courtAttemptIdFromHostContext, runDirectoryFromHostContext, } from "./host-contracts.js";
|
|
4
5
|
import { runIdFromRunDirectory } from "./run-terminal-artifacts.js";
|
|
@@ -491,6 +492,20 @@ export function createSubmissionLedgerHost(host, outputTools, failInfrastructure
|
|
|
491
492
|
? { lens: rawLens }
|
|
492
493
|
: {}),
|
|
493
494
|
});
|
|
495
|
+
// Native structured-output hosts have no tool-result reply to the model.
|
|
496
|
+
// Keep the conversation open only after the receipt is durably sealed.
|
|
497
|
+
const runDirectory = runDirectoryFromHostContext(context);
|
|
498
|
+
if (context.host !== "claude" && context.host !== "codex"
|
|
499
|
+
&& runDirectory !== undefined && isUnboundRunDirectory(runDirectory)) {
|
|
500
|
+
result = {
|
|
501
|
+
...result,
|
|
502
|
+
terminate: false,
|
|
503
|
+
content: [...result.content, {
|
|
504
|
+
type: "text",
|
|
505
|
+
text: (await readPackageMaterial("resources/ticket-at-receipt.md")).trim(),
|
|
506
|
+
}],
|
|
507
|
+
};
|
|
508
|
+
}
|
|
494
509
|
}
|
|
495
510
|
catch (error) {
|
|
496
511
|
if (toolThrew)
|
package/package.json
CHANGED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
卷已收下,不用重交。本腿还没有票号:知道就调 ak_report_ticket 报上;不知道就直接结束。
|
package/souls/audit-law.md
CHANGED
|
@@ -11,6 +11,8 @@
|
|
|
11
11
|
|
|
12
12
|
一切取证席位的工具与方式不受限:读、写、装、跑、下探针皆可。唯一义务是**用毕清理干净并如实记载**,包括清理本次取证在 worktree 与临时目录之外创建的任何东西。
|
|
13
13
|
|
|
14
|
+
主张不是证据;出自机器执行、而非任何 LLM 之口的确定性事实,核指针加抽查即可。施工席或前一站跑测试的那次调用,命令、退出码、输出由宿主记进会话卷,即属此类:跑时工作树干净、HEAD 与测试组都没变的,打开指针核对命令、HEAD、退出码和输出,再从那组里亲手跑一条,不原样重跑全组。变异探针和为找问题加跑的测试照旧。
|
|
15
|
+
|
|
14
16
|
## 审官纪律
|
|
15
17
|
|
|
16
18
|
finding 提**类别**;实例只作证据。一次找齐全,不逐轮抽样。
|
package/souls/judge.md
CHANGED
|
@@ -6,7 +6,7 @@ Judge Soul(大理寺)
|
|
|
6
6
|
三、核第二法源:如有违反第一法源且没有陛下原话授权的,直接上呈。特别是给你的prompt,最容易出现问题,要逐句核对和陛下的话有没有出入,特别是让你缩小范围,只看局部,要求只看上一轮或者特定问题的。如有立即上呈打回
|
|
7
7
|
四、核案。 全审本次授权的 target/range。计划、处置清单、packet、代码、测试都是案面,举证责任由材料决定,不由派单指定。
|
|
8
8
|
每项改动须有票面依据,每项要求须有落实或明确处置;相抵、夹带、缺口一并查(标不出来的即为夹带;有要求而清单没有的即为缺口(夹带的默认值、护栏、平行机制、仅测试用的生产钩子同论)),未授权改治理法文件,未声明的生产默认行为同论。清单不全不得收敛。复审亦全审授权范围,上轮问题不划定本轮案面。
|
|
9
|
-
五、裁断。 主张不是证据。一切 LLM 主张须亲验;当前事实只认当前 head,修复者自述不能替代新 head
|
|
9
|
+
五、裁断。 主张不是证据。一切 LLM 主张须亲验;当前事实只认当前 head,修复者自述不能替代新 head 上的机械验证与独立复查。机器执行的事实怎么核,见《审法·取证授权》。
|
|
10
10
|
事实与处方分判:事实成立而处方违法源,保留事实,驳回处方或另开合法处方。 每条问题显式记录和处置,严重度由你裁定,不因级别低而遗漏或暗降;裁决与驳回须锚定法源和证据。依当前一手证据独立双向抽样重推,误删与该删未删同查。
|
|
11
11
|
测试质量须亲审。取证与同类复核依《审法》;复杂度、测试质量与成本依《质量法》,不另立一套标准。
|
|
12
12
|
删简根因优先。护栏类 finding(防误用、交错、边角的检查或绑定)无真实可复现的失败或历史实例的,按过度防御驳回,不列 P1;票面明写不做的机制,处方不得开(越权加戏)。批准护栏须说明不变式所属接缝及为何删简根因不足。
|
package/souls/quality-law.md
CHANGED
|
@@ -51,7 +51,7 @@
|
|
|
51
51
|
|
|
52
52
|
名实必须相符:占着小型名义、消耗大型资源的测试,按错位缺陷处理。重型名单单列、串行调度,入列逐个有理由。
|
|
53
53
|
|
|
54
|
-
|
|
54
|
+
日常施工只跑触及面的聚焦测试,交卷时写明最后一次聚焦测试那次调用的指针(runId、toolCallId)和跑时的 HEAD;全量到绿只在家族或批次的**最终待合并状态**跑一次——**家族车道上的逐腿交卷不是收尾,不得据此索取全量**;之后再有修复,在新的最终状态重跑全量。
|
|
55
55
|
|
|
56
56
|
**最终待合并状态**只有一个:本票或本家族全部轮次结束、判官已 converged、PR 已开之后,即将并入目标分支的那一个 HEAD;CI 在它上面通过一次即为全量。其后再有修复 commit,新 HEAD 才是新的最终状态。将作监/修内司的每次交卷(含闸内重交)、切片轮次、修理轮次、审官中间轮次都不是最终状态——审官不得据此索全量,派单也不得把本机全量写进这些轮次的验收。
|
|
57
57
|
|
|
@@ -4,10 +4,7 @@
|
|
|
4
4
|
* starts two ordinary single-axis runs here.
|
|
5
5
|
*/
|
|
6
6
|
import { dirname, resolve } from "node:path";
|
|
7
|
-
import { MISSING_TICKET_REASK_MATERIAL } from "../report-ticket-tool.ts";
|
|
8
7
|
import { isUnboundRunDirectory, sessionFileOf } from "../role-run-placement.ts";
|
|
9
|
-
import { rewriteRunDirectoryPathValue } from "../role-run-relocation.ts";
|
|
10
|
-
import { readPackageMaterial } from "../session-opening-materials.ts";
|
|
11
8
|
import { createPiDoctorAuditor } from "../doctor-auditor.ts";
|
|
12
9
|
|
|
13
10
|
import type { DurablePrincipalAuthority, HostContext, RoleTurnRequest } from "../host-contracts.ts";
|
|
@@ -193,21 +190,10 @@ function unreadablePostSubmissionStatus(
|
|
|
193
190
|
* status: the caller presents the volumes already in hand and does not advance
|
|
194
191
|
* review. ADR 0007 audit continues are not this shape.
|
|
195
192
|
*/
|
|
196
|
-
/**
|
|
197
|
-
* Drop turn-scoped soft-reask identity; keep cross-turn budgets and ownership cell.
|
|
198
|
-
* #1171 F2-R5 / F2-R9: ordinary status reask and court continue must not inherit
|
|
199
|
-
* softTicketReaskTurn (that flag only governs the soft-reask turn's own settle).
|
|
200
|
-
*/
|
|
201
|
-
function withoutSoftTicketReaskTurn(env: InstructionSeatRunEnv): InstructionSeatRunEnv {
|
|
202
|
-
const { softTicketReaskTurn: _softTicketReaskTurn, ...rest } = env;
|
|
203
|
-
return rest;
|
|
204
|
-
}
|
|
205
|
-
|
|
206
193
|
function withUnreadableReask(env: InstructionSeatRunEnv): InstructionSeatRunEnv | undefined {
|
|
207
194
|
const spent = env.unreadableReasksSpent ?? 0;
|
|
208
195
|
if (spent >= deliveryLimitFromConfig(env.autoResumeLimit)) return undefined;
|
|
209
|
-
|
|
210
|
-
return { ...withoutSoftTicketReaskTurn(env), unreadableReasksSpent: spent + 1 };
|
|
196
|
+
return { ...env, unreadableReasksSpent: spent + 1 };
|
|
211
197
|
}
|
|
212
198
|
|
|
213
199
|
/** Budget spent on an unreadable status. Distinct from "status is readable". */
|
|
@@ -406,144 +392,12 @@ async function reaskUnreadablePostSubmissionStatus(
|
|
|
406
392
|
);
|
|
407
393
|
}
|
|
408
394
|
|
|
409
|
-
/**
|
|
410
|
-
* Soft-reask call outcome at the missing-ticket seam (#1171 F2-R6 / F2-R7).
|
|
411
|
-
* Ownership is decided here once from this nested chain's settlement evidence
|
|
412
|
-
* (soft-reask turn sealed → ownership cell; or returned terminal still carries volume);
|
|
413
|
-
* callers must not re-judge the same criterion.
|
|
414
|
-
*/
|
|
415
|
-
type SoftTicketReaskOutcome =
|
|
416
|
-
| { readonly kind: "none" }
|
|
417
|
-
| { readonly kind: "nested_owns"; readonly result: SeatRunResult }
|
|
418
|
-
| { readonly kind: "no_substitute"; readonly result: SeatRunResult };
|
|
419
|
-
|
|
420
|
-
/**
|
|
421
|
-
* #1171: sealed submission still leaves the leg unbound → soft reask once.
|
|
422
|
-
* Does not reject the sealed receipt; exhaustion leaves unbound as filed.
|
|
423
|
-
* Reask copy lives in package resources (ADR 0073).
|
|
424
|
-
* #1171 F2-R7: ownership comes from this nested resume's settlement seam
|
|
425
|
-
* (softTicketReaskOwnership.sealedSubstitute set when the soft-reask turn itself seals),
|
|
426
|
-
* not full-leg sealed count / time-window deltas that can absorb another court's seal.
|
|
427
|
-
*/
|
|
428
|
-
async function reaskMissingTicketOnce(
|
|
429
|
-
admitted: AdmittedRoleInvocation,
|
|
430
|
-
terminal: TerminalResult | undefined,
|
|
431
|
-
env: InstructionSeatRunEnv,
|
|
432
|
-
io: CliIo,
|
|
433
|
-
): Promise<SoftTicketReaskOutcome> {
|
|
434
|
-
if ((env.ticketReasksSpent ?? 0) >= 1) return { kind: "none" };
|
|
435
|
-
// #1192: a table lookup has no one to ask, and a second turn would re-seal.
|
|
436
|
-
if (isNavigatorSeat(admitted.role)) return { kind: "none" };
|
|
437
|
-
if (admitted.ticketNumber !== undefined) return { kind: "none" };
|
|
438
|
-
if (!isUnboundRunDirectory(admitted.runDirectory)) return { kind: "none" };
|
|
439
|
-
if (terminal?.roleOutcome.kind !== "accepted" && terminal?.roleOutcome.kind !== "audit_escalation") {
|
|
440
|
-
return { kind: "none" };
|
|
441
|
-
}
|
|
442
|
-
const payloads = terminal.roleOutcome.payloads ?? [];
|
|
443
|
-
if (payloads.length === 0) return { kind: "none" };
|
|
444
|
-
// Sealed-with-ticket skips via admitted.ticketNumber after bindSealedTicketNumber
|
|
445
|
-
// (post-admission). Do not re-parse payload.ticketNumber here — that would
|
|
446
|
-
// duplicate the existing ticket seam (#1171 notary: no rule copy).
|
|
447
|
-
const instruction = (await readPackageMaterial(MISSING_TICKET_REASK_MATERIAL)).trim();
|
|
448
|
-
// Spend budget and mark this turn as the soft reask (settlement identity).
|
|
449
|
-
// Ownership cell is set at that turn's settlement; env spreads keep the same
|
|
450
|
-
// ref through audit continue — see envForCourtContinue (#1171 F2-R5 / F2-R7).
|
|
451
|
-
const softTicketReaskOwnership = { sealedSubstitute: false };
|
|
452
|
-
const softReaskEnv: InstructionSeatRunEnv = {
|
|
453
|
-
...env,
|
|
454
|
-
ticketReasksSpent: 1,
|
|
455
|
-
softTicketReaskTurn: true,
|
|
456
|
-
softTicketReaskOwnership,
|
|
457
|
-
};
|
|
458
|
-
// Named #1171 missing-ticket: summons opens a new court; message carries the
|
|
459
|
-
// package reask copy (not arbitrary summons.instruction passthrough).
|
|
460
|
-
const result = await runPublicInstructionSeatResume(
|
|
461
|
-
{
|
|
462
|
-
runId: admitted.runId,
|
|
463
|
-
runDirectory: admitted.runDirectory,
|
|
464
|
-
message: instruction,
|
|
465
|
-
summons: { instructionEmpty: true },
|
|
466
|
-
},
|
|
467
|
-
softReaskEnv,
|
|
468
|
-
io,
|
|
469
|
-
);
|
|
470
|
-
if (
|
|
471
|
-
softTicketReaskOwnership.sealedSubstitute
|
|
472
|
-
|| terminalCarriesSealedSubmission(result.terminal)
|
|
473
|
-
) {
|
|
474
|
-
return { kind: "nested_owns", result };
|
|
475
|
-
}
|
|
476
|
-
return { kind: "no_substitute", result };
|
|
477
|
-
}
|
|
478
|
-
|
|
479
|
-
/** Court continue: keep cross-turn budgets + ownership cell; drop turn-scoped soft-reask identity. */
|
|
395
|
+
/** Court continue resets the unreadable-status budget. */
|
|
480
396
|
function envForCourtContinue(env: InstructionSeatRunEnv): InstructionSeatRunEnv {
|
|
481
|
-
return { ...
|
|
482
|
-
}
|
|
483
|
-
|
|
484
|
-
/** True when a terminal still carries a sealed accepted / audit_escalation volume. */
|
|
485
|
-
function terminalCarriesSealedSubmission(terminal: TerminalResult | undefined): boolean {
|
|
486
|
-
const kind = terminal?.roleOutcome.kind;
|
|
487
|
-
if (kind !== "accepted" && kind !== "audit_escalation") return false;
|
|
488
|
-
return (terminal?.roleOutcome.payloads ?? []).length > 0;
|
|
489
|
-
}
|
|
490
|
-
|
|
491
|
-
/**
|
|
492
|
-
* #1171 F2-R2: kept original terminal may still name pre-relocate artifact
|
|
493
|
-
* paths after a report-only reask moved the leg. Project onto live admitted
|
|
494
|
-
* via the same rewrite seam post-admission uses after relocate.
|
|
495
|
-
*/
|
|
496
|
-
function projectKeptOriginalArtifactsOntoLiveAdmitted(
|
|
497
|
-
original: SeatRunResult,
|
|
498
|
-
kept: SeatRunResult,
|
|
499
|
-
): void {
|
|
500
|
-
const oldRunDirectory = original.admitted?.runDirectory;
|
|
501
|
-
const newRunDirectory = kept.admitted?.runDirectory;
|
|
502
|
-
if (
|
|
503
|
-
oldRunDirectory === undefined
|
|
504
|
-
|| newRunDirectory === undefined
|
|
505
|
-
|| oldRunDirectory === newRunDirectory
|
|
506
|
-
|| kept.terminal === undefined
|
|
507
|
-
) {
|
|
508
|
-
return;
|
|
509
|
-
}
|
|
510
|
-
for (const artifact of kept.terminal.artifacts) {
|
|
511
|
-
artifact.path = rewriteRunDirectoryPathValue(
|
|
512
|
-
artifact.path,
|
|
513
|
-
oldRunDirectory,
|
|
514
|
-
newRunDirectory,
|
|
515
|
-
) as string;
|
|
516
|
-
}
|
|
397
|
+
return { ...env, unreadableReasksSpent: 0 };
|
|
517
398
|
}
|
|
518
399
|
|
|
519
|
-
/**
|
|
520
|
-
* #1171 F2: missing-ticket reask did not seal a substitute — keep original
|
|
521
|
-
* terminal; take live placement from the reask when it relocated.
|
|
522
|
-
* #1171 F2-R1: silence / report-only stays on the original chain; a real host
|
|
523
|
-
* failure (nonzero exit) must be delivered honestly — do not wash it into the
|
|
524
|
-
* original accepted result. Sealed original volume remains on disk either way.
|
|
525
|
-
* #1171 F2-R2: after keep, project report artifact paths onto the live
|
|
526
|
-
* admitted directory so public result, durable face, and artifact refs agree.
|
|
527
|
-
* Substitute ownership is decided once at the soft-reask call seam (F2-R6);
|
|
528
|
-
* this helper only finishes the keep path.
|
|
529
|
-
*/
|
|
530
|
-
function mergeMissingTicketReaskKeepOriginal(
|
|
531
|
-
original: SeatRunResult,
|
|
532
|
-
ticketReask: SeatRunResult,
|
|
533
|
-
): SeatRunResult {
|
|
534
|
-
if (ticketReask.exitCode !== 0) return ticketReask;
|
|
535
|
-
const admitted = ticketReask.admitted ?? original.admitted;
|
|
536
|
-
if (admitted === undefined) return { ...original };
|
|
537
|
-
const kept = { ...original, admitted };
|
|
538
|
-
projectKeptOriginalArtifactsOntoLiveAdmitted(original, kept);
|
|
539
|
-
return kept;
|
|
540
|
-
}
|
|
541
|
-
|
|
542
|
-
/**
|
|
543
|
-
* Post-submission status reask, then missing-ticket soft reask, then audit.
|
|
544
|
-
* #1171 F3: status exhaustion must not skip the one missing-ticket reask.
|
|
545
|
-
* #1171 F2: ticket reask without a substitute seal continues the original chain.
|
|
546
|
-
*/
|
|
400
|
+
/** Post-submission status reask, then audit. */
|
|
547
401
|
async function continueAfterPostSubmissionGuards(
|
|
548
402
|
result: SeatRunResult,
|
|
549
403
|
admittedRole: PackagedRole,
|
|
@@ -562,44 +416,22 @@ async function continueAfterPostSubmissionGuards(
|
|
|
562
416
|
env,
|
|
563
417
|
io,
|
|
564
418
|
);
|
|
565
|
-
let
|
|
419
|
+
let continued = result;
|
|
566
420
|
if (isUnreadableReaskExhausted(unreadableReask)) {
|
|
567
421
|
const terminal = result.terminal === undefined
|
|
568
422
|
? undefined
|
|
569
423
|
: withUnsettledDirection(result.terminal);
|
|
570
|
-
|
|
424
|
+
continued = terminal === undefined
|
|
571
425
|
? { ...result, admitted: live }
|
|
572
426
|
: { ...result, admitted: live, terminal };
|
|
573
|
-
// Fall through: missing-ticket reask still owed on the original sealed volume.
|
|
574
427
|
} else if (unreadableReask !== undefined) {
|
|
575
428
|
return unreadableReask;
|
|
576
429
|
}
|
|
577
|
-
const forTicket = afterStatus.admitted ?? live;
|
|
578
|
-
// Ownership is decided once inside reaskMissingTicketOnce (#1171 F2-R6).
|
|
579
|
-
const softReask = await reaskMissingTicketOnce(
|
|
580
|
-
forTicket,
|
|
581
|
-
afterStatus.terminal ?? result.terminal,
|
|
582
|
-
env,
|
|
583
|
-
io,
|
|
584
|
-
);
|
|
585
|
-
if (softReask.kind === "nested_owns") {
|
|
586
|
-
// Nested resume already finished guards + audit/present for the substitute.
|
|
587
|
-
// Do not run a second outer settlement of the same public call (#1171 F2-R4b).
|
|
588
|
-
return softReask.result;
|
|
589
|
-
}
|
|
590
|
-
const continued = softReask.kind === "none"
|
|
591
|
-
? afterStatus
|
|
592
|
-
: mergeMissingTicketReaskKeepOriginal(afterStatus, softReask.result);
|
|
593
430
|
const continuedAdmitted = continued.admitted ?? live;
|
|
594
|
-
// Spent budget rides the outer audit/continue chain (#1171 F2-R4a). Soft-reask
|
|
595
|
-
// settlement identity does not — ordinary continue turns publish no_receipt (#1171 F2-R5).
|
|
596
|
-
const continuedEnv = softReask.kind !== "none"
|
|
597
|
-
? { ...env, ticketReasksSpent: 1 }
|
|
598
|
-
: env;
|
|
599
431
|
if (AUDITED_ROLES.has(admittedRole) && continued.terminal?.roleOutcome.kind === "accepted") {
|
|
600
432
|
return auditSubmittedRole(
|
|
601
433
|
{ ...continued, admitted: continuedAdmitted },
|
|
602
|
-
|
|
434
|
+
env,
|
|
603
435
|
io,
|
|
604
436
|
);
|
|
605
437
|
}
|
|
@@ -1168,8 +1000,7 @@ export async function runPublicInstructionSeat(
|
|
|
1168
1000
|
const result = await runCountersignBody(parsed, env, {
|
|
1169
1001
|
stdout: (value) => held.push(value), stderr: io.stderr,
|
|
1170
1002
|
});
|
|
1171
|
-
//
|
|
1172
|
-
// soft reask once, then audit — no early return that skips the reask.
|
|
1003
|
+
// Apply the same post-submission guards and audit as every other seat.
|
|
1173
1004
|
return continueAfterPostSubmissionGuards(result, "countersign", env, io, held);
|
|
1174
1005
|
}
|
|
1175
1006
|
|
|
@@ -1345,9 +1176,7 @@ export async function runPublicInstructionSeat(
|
|
|
1345
1176
|
await persistAdmittedSourceRunPath(admitted, auditorSource, auditorSubject);
|
|
1346
1177
|
admitted = { ...admitted, sourceRunPath: auditorSource } as AdmittedRoleInvocation;
|
|
1347
1178
|
}
|
|
1348
|
-
// #1208 C2: same helper as resume —
|
|
1349
|
-
// source binds and relocates before the missing-ticket soft reask can
|
|
1350
|
-
// fire. True no-ticket sources stay unbound (#1171 asks once).
|
|
1179
|
+
// #1208 C2: same helper as resume — inherit the audited source's known ticket.
|
|
1351
1180
|
admitted = await inheritKnownTicketFromSource(admitted, env.principalAuthority);
|
|
1352
1181
|
|
|
1353
1182
|
const runAdmitted = async (): Promise<SeatRunResult> => {
|
|
@@ -1736,8 +1565,6 @@ async function auditSubmittedRole(
|
|
|
1736
1565
|
resumedOfficer?: AdmittedRoleInvocation,
|
|
1737
1566
|
passedReceipt?: unknown,
|
|
1738
1567
|
): Promise<SeatRunResult> {
|
|
1739
|
-
// Ordinary audit chain: drop soft-reask turn identity; keep budgets/ownership (#1171 F2-R9).
|
|
1740
|
-
env = withoutSoftTicketReaskTurn(env);
|
|
1741
1568
|
const admitted = turn.admitted;
|
|
1742
1569
|
if (admitted === undefined || !AUDITED_ROLES.has(admitted.role)
|
|
1743
1570
|
|| turn.terminal?.roleOutcome.kind !== "accepted") return turn;
|
|
@@ -449,23 +449,6 @@ export type PostAdmissionEnv = {
|
|
|
449
449
|
* keeps this env's autoResumeLimit; the counter is the only thing that moves.
|
|
450
450
|
*/
|
|
451
451
|
unreadableReasksSpent?: number;
|
|
452
|
-
/**
|
|
453
|
-
* #1171: missing-ticket soft reasks already issued on this chain. Hard ceiling
|
|
454
|
-
* is one — still no ticket after that stays unbound with the sealed receipt.
|
|
455
|
-
* Budget only across rounds; not the current turn's settlement identity.
|
|
456
|
-
*/
|
|
457
|
-
ticketReasksSpent?: number;
|
|
458
|
-
/**
|
|
459
|
-
* #1171 F2-R5: this turn is the one soft reask. Keep the sealed terminal face
|
|
460
|
-
* when it ends without a substitute seal. Not carried into audit continue.
|
|
461
|
-
*/
|
|
462
|
-
softTicketReaskTurn?: true;
|
|
463
|
-
/**
|
|
464
|
-
* #1171 F2-R7: live ownership cell for this soft-reask nested chain.
|
|
465
|
-
* Created at soft-reask dispatch; settlement of that turn sets sealedSubstitute.
|
|
466
|
-
* Shared ref survives env spreads — not a second ledger.
|
|
467
|
-
*/
|
|
468
|
-
softTicketReaskOwnership?: { sealedSubstitute: boolean };
|
|
469
452
|
createRunId?: () => string;
|
|
470
453
|
/**
|
|
471
454
|
* Parent cancellation for a nested public summon (#675). Every dispatched turn
|
|
@@ -1220,20 +1203,7 @@ export async function dispatchPostAdmissionTurn<
|
|
|
1220
1203
|
result.terminal === pendingTerminal
|
|
1221
1204
|
) {
|
|
1222
1205
|
try {
|
|
1223
|
-
|
|
1224
|
-
// preserves the sealed terminal face. ticketReasksSpent is budget across
|
|
1225
|
-
// rounds — after audit continue, ordinary no-volume turns publish
|
|
1226
|
-
// no_receipt honestly. previewOnly skips the terminal pointer;
|
|
1227
|
-
// recordAttemptHistory still appends.
|
|
1228
|
-
const softTicketReaskNoSeal =
|
|
1229
|
-
pendingSettlement !== "sealed" && env.softTicketReaskTurn === true;
|
|
1230
|
-
const noSealScope = softTicketReaskNoSeal
|
|
1231
|
-
? {
|
|
1232
|
-
...courtScope,
|
|
1233
|
-
previewOnly: true as const,
|
|
1234
|
-
recordAttemptHistory: true as const,
|
|
1235
|
-
}
|
|
1236
|
-
: { ...courtScope, recordAttemptHistory: true as const };
|
|
1206
|
+
const noSealScope = { ...courtScope, recordAttemptHistory: true as const };
|
|
1237
1207
|
const terminal = pendingSettlement === "sealed"
|
|
1238
1208
|
? await adapters.trySettle(admitted, env.principalAuthority,
|
|
1239
1209
|
{ ...courtScope, recordAttemptHistory: true })
|
|
@@ -1243,12 +1213,6 @@ export async function dispatchPostAdmissionTurn<
|
|
|
1243
1213
|
noSealScope);
|
|
1244
1214
|
if (terminal === undefined) throw new Error("settled host attempt vanished before publication");
|
|
1245
1215
|
result = { ...result, terminal, exitCode: exitCodeForTerminalOutcome(terminal.roleOutcome) };
|
|
1246
|
-
// #1171 F2-R7: ownership evidence at this soft-reask turn's seal —
|
|
1247
|
-
// mutate the live ownership cell carried on env (survives spreads).
|
|
1248
|
-
if (pendingSettlement === "sealed" && env.softTicketReaskTurn === true
|
|
1249
|
-
&& env.softTicketReaskOwnership !== undefined) {
|
|
1250
|
-
env.softTicketReaskOwnership.sealedSubstitute = true;
|
|
1251
|
-
}
|
|
1252
1216
|
if (pendingSettlement === "sealed" && terminal.roleOutcome.kind === "accepted"
|
|
1253
1217
|
&& request.courtAttemptId !== undefined && request.courtAttemptId.length > 0) {
|
|
1254
1218
|
try {
|
|
@@ -40,8 +40,7 @@ const reportTicketArgsSchema = Type.Object(
|
|
|
40
40
|
|
|
41
41
|
export const REPORT_TICKET_TOOL_DESCRIPTION =
|
|
42
42
|
"向包申报本票号。已知票号时开工第一件事调用;包当场把本腿归到票号目录。"
|
|
43
|
-
+ "尚无票号(例如中书省建票前)不要调用,取得后再报。"
|
|
44
|
-
+ "交卷时若仍未报过且交卷亦未带票号,包会经既有重交通道再问一次。";
|
|
43
|
+
+ "尚无票号(例如中书省建票前)不要调用,取得后再报。";
|
|
45
44
|
|
|
46
45
|
function pageString(page: Record<string, unknown> | undefined, key: string): string | undefined {
|
|
47
46
|
const value = page?.[key];
|
|
@@ -199,6 +198,3 @@ export function registerReportTicketTool(
|
|
|
199
198
|
},
|
|
200
199
|
});
|
|
201
200
|
}
|
|
202
|
-
|
|
203
|
-
/** Soft re-ask when a sealed submission still leaves the leg unbound (#1171 / ADR 0073). */
|
|
204
|
-
export const MISSING_TICKET_REASK_MATERIAL = "resources/missing-ticket-reask.md" as const;
|
package/src/submission-ledger.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { sessionFileIn, sessionFileOf } from "./role-run-placement.ts";
|
|
1
|
+
import { isUnboundRunDirectory, sessionFileIn, sessionFileOf } from "./role-run-placement.ts";
|
|
2
|
+
import { readPackageMaterial } from "./session-opening-materials.ts";
|
|
2
3
|
|
|
3
4
|
import {
|
|
4
5
|
resolveActivationLedgerHome,
|
|
@@ -676,6 +677,20 @@ export function createSubmissionLedgerHost(
|
|
|
676
677
|
? { lens: rawLens }
|
|
677
678
|
: {}),
|
|
678
679
|
});
|
|
680
|
+
// Native structured-output hosts have no tool-result reply to the model.
|
|
681
|
+
// Keep the conversation open only after the receipt is durably sealed.
|
|
682
|
+
const runDirectory = runDirectoryFromHostContext(context);
|
|
683
|
+
if (context.host !== "claude" && context.host !== "codex"
|
|
684
|
+
&& runDirectory !== undefined && isUnboundRunDirectory(runDirectory)) {
|
|
685
|
+
result = {
|
|
686
|
+
...result,
|
|
687
|
+
terminate: false,
|
|
688
|
+
content: [...result.content, {
|
|
689
|
+
type: "text",
|
|
690
|
+
text: (await readPackageMaterial("resources/ticket-at-receipt.md")).trim(),
|
|
691
|
+
}],
|
|
692
|
+
};
|
|
693
|
+
}
|
|
679
694
|
} catch (error) {
|
|
680
695
|
if (toolThrew) throw error;
|
|
681
696
|
return failInfrastructure(error, context, toolCallId);
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
本腿仍无票号。请调用 ak_report_ticket 申报,或在交卷里带上 ticketNumber。只问这一次。
|