@akagilnc/pi-workflow-roles 0.1.2068 → 0.1.2076

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.
@@ -73,21 +73,16 @@ import {
73
73
  type PackagedMethodSkillProvenance,
74
74
  } from "../package-resources/method-skill.ts";
75
75
  import {
76
- bindCurrentDurableTerminalToMarker,
77
76
  classifyPackagedRoleTerminalResult,
77
+ findLatestDurablePackagedRoleTerminal,
78
78
  isAcceptedPackagedRoleTerminalResult,
79
79
  isReceiptSettlementBindingClear,
80
- markerMatchesExpectedIdentity,
81
- type ExpectedInvocationIdentity,
80
+ NAVIGATOR_INVOCATION_ENTRY,
81
+ parseInvocationMarkerIdentity,
82
82
  type InvocationMarkerIdentity,
83
83
  } from "../navigator-invocation-identity.ts";
84
84
  import type { NavigatorPhase } from "../navigator-attendance.ts";
85
85
  import { NO_RECEIPT_LIFECYCLE_ENTRY_TYPE, parseNoReceiptLifecycleFacts, type NoReceiptLifecycleFacts } from "../receipt-delivery-policy.ts";
86
- import { packagedRoleMetadata } from "../packaged-role-registry.ts";
87
- import {
88
- workSubjectKeyFromProjectRoot,
89
- workSubjectKeysEqual,
90
- } from "../work-subject-identity.ts";
91
86
  import {
92
87
  ensureRunArtifactsDir,
93
88
  type AdmittedCoderInvocation,
@@ -493,12 +488,6 @@ type SessionEntry = {
493
488
  parentSession?: string;
494
489
  };
495
490
 
496
- /** Optional independent identity from admitted/shared lifecycle (not attendance self-fields). */
497
- export type NavigatorAttendanceIdentity = {
498
- readonly phase?: NavigatorPhase;
499
- readonly subjectKey?: string;
500
- };
501
-
502
491
  function isMissingPathError(error: unknown): boolean {
503
492
  return (
504
493
  error instanceof Error &&
@@ -2001,91 +1990,20 @@ function navigatorPhaseValue(value: unknown): NavigatorPhase {
2001
1990
  return null;
2002
1991
  }
2003
1992
 
2004
- function attendanceIdentityFromAdmitted(
2005
- admitted: AdmittedRoleInvocation,
2006
- ): NavigatorAttendanceIdentity {
2007
- // Public CLI sessions live under the machine ledger; Navigator derives subject from
2008
- // the project/cwd work identity, not the per-run session directory spelling.
2009
- const subjectKey = workSubjectKeyFromProjectRoot(admitted.projectRoot);
2010
- if (admitted.role === "coder" || admitted.role === "fixer") {
2011
- return { phase: admitted.phase, subjectKey };
2012
- }
2013
- return { phase: null, subjectKey };
2014
- }
2015
-
2016
1993
  /**
2017
- * Independent expected role/phase/subject for marker correlation.
2018
- * Role comes from the durable packaged terminal tool; phase/subject from admitted
2019
- * lifecycle, Developer session cwd, or registry — never attendance self-fields.
2020
- */
2021
- function independentExpectedIdentity(
2022
- entries: readonly SessionEntry[],
2023
- terminalRole: string,
2024
- supplied?: NavigatorAttendanceIdentity,
2025
- ): ExpectedInvocationIdentity {
2026
- let subjectKey: string | undefined;
2027
- for (const entry of entries) {
2028
- if (entry?.type !== "session") continue;
2029
- if (typeof entry.cwd === "string" && entry.cwd.trim() !== "") {
2030
- subjectKey = workSubjectKeyFromProjectRoot(entry.cwd);
2031
- }
2032
- break;
2033
- }
2034
- if (typeof supplied?.subjectKey === "string") {
2035
- subjectKey = supplied.subjectKey;
2036
- }
2037
-
2038
- let phase: NavigatorPhase | undefined;
2039
- let allowedPhases: readonly NavigatorPhase[] | undefined;
2040
- // null is a real phase fact (Judge/Reviewer/…); only omit when not independently known.
2041
- if (supplied !== undefined && Object.hasOwn(supplied, "phase")) {
2042
- phase = supplied.phase ?? null;
2043
- } else {
2044
- const meta = packagedRoleMetadata(terminalRole);
2045
- if (meta !== undefined) {
2046
- if (meta.phases.length === 1) {
2047
- phase = meta.phases[0] as NavigatorPhase;
2048
- } else {
2049
- allowedPhases = meta.phases as readonly NavigatorPhase[];
2050
- }
2051
- }
2052
- }
2053
-
2054
- return {
2055
- role: terminalRole,
2056
- ...(phase !== undefined ? { phase } : {}),
2057
- ...(allowedPhases !== undefined ? { allowedPhases } : {}),
2058
- ...(subjectKey !== undefined ? { subjectKey } : {}),
2059
- };
2060
- }
2061
-
2062
- /**
2063
- * Attendance must match the bound marker identity and current durable terminal role.
2064
- * Self-shape of attendance fields is not correlation. Marker already matched the
2065
- * independent expected identity before this check runs.
1994
+ * Minimal attendance provenance against the bound marker (ADR 0043).
1995
+ * Keep only invocationId + post-terminal ordering. Runtime-self-produced
1996
+ * role/phase/subject/version are not re-reconciled here (ADR 0042).
2066
1997
  */
2067
1998
  function navigatorAttendanceCorrelatedWithBoundMarker(
2068
1999
  details: Record<string, unknown>,
2069
2000
  attendanceIndex: number,
2070
- terminal: { index: number; role: string },
2001
+ terminalIndex: number,
2071
2002
  marker: InvocationMarkerIdentity,
2072
2003
  ): boolean {
2073
- if (attendanceIndex <= terminal.index) return false;
2074
- if (details.version !== 1) return false;
2075
-
2076
- // Role comes from the packaged terminal tool — compare, do not self-validate.
2077
- if (details.role !== terminal.role) return false;
2078
- // Marker role must already equal terminal role (checked by caller); attendance follows marker.
2079
- if (details.role !== marker.role) return false;
2080
-
2004
+ if (attendanceIndex <= terminalIndex) return false;
2081
2005
  // Exact current invocation token is the bound marker principal.
2082
2006
  if (details.invocationId !== marker.invocationId) return false;
2083
-
2084
- // Phase and subject ride the same marker identity truth table.
2085
- if (details.phase !== marker.phase) return false;
2086
- if (typeof details.subjectKey !== "string") return false;
2087
- if (!workSubjectKeysEqual(details.subjectKey, marker.subjectKey)) return false;
2088
-
2089
2007
  return true;
2090
2008
  }
2091
2009
 
@@ -2157,45 +2075,37 @@ function parseNavigatorAttendanceDetails(
2157
2075
 
2158
2076
  export function extractNavigatorFact(
2159
2077
  entries: readonly SessionEntry[],
2160
- identity?: NavigatorAttendanceIdentity,
2161
2078
  ): TerminalNavigatorFact {
2162
2079
  // Affirmative attendance only. Missing / uncorrelated / unparseable is never no-advice.
2163
- // One truth table: durable classifier + singleton marker binding + marker identity match.
2164
- const binding = bindCurrentDurableTerminalToMarker(entries);
2165
- if (binding.kind === "absent") {
2080
+ // Minimal provenance: latest durable terminal + nearest preceding marker +
2081
+ // invocationId + post-terminal order. Marker↔terminal cardinality belongs to
2082
+ // receipt settlement (isReceiptSettlementBindingClear), not attendance extraction.
2083
+ const terminal = findLatestDurablePackagedRoleTerminal(entries);
2084
+ if (terminal === undefined) {
2166
2085
  return {
2167
2086
  disposition: "unavailable",
2168
2087
  source: "unknown",
2169
2088
  reason: "Navigator attendance has no durable packaged role terminal",
2170
2089
  };
2171
2090
  }
2172
- if (binding.kind === "ambiguous") {
2173
- return {
2174
- disposition: "unavailable",
2175
- source: "unknown",
2176
- reason: "Navigator attendance is ambiguous across multiple durable role terminals",
2177
- };
2178
- }
2179
- if (binding.kind === "unbound") {
2180
- return {
2181
- disposition: "unavailable",
2182
- source: "unknown",
2183
- reason: "Navigator attendance is uncorrelated with session invocation facts",
2184
- };
2185
- }
2186
2091
 
2187
- const { terminal, marker } = binding;
2188
- // Marker role must match the durable terminal tool's role.
2189
- if (marker.role !== terminal.role) {
2092
+ let markerIndex = -1;
2093
+ for (let i = terminal.index - 1; i >= 0; i -= 1) {
2094
+ const entry = entries[i];
2095
+ if (entry?.type === "custom" && entry.customType === NAVIGATOR_INVOCATION_ENTRY) {
2096
+ markerIndex = i;
2097
+ break;
2098
+ }
2099
+ }
2100
+ if (markerIndex < 0) {
2190
2101
  return {
2191
2102
  disposition: "unavailable",
2192
2103
  source: "unknown",
2193
2104
  reason: "Navigator attendance is uncorrelated with session invocation facts",
2194
2105
  };
2195
2106
  }
2196
- // Marker phase/subject (and role) must match independently admitted expected identity.
2197
- const expected = independentExpectedIdentity(entries, terminal.role, identity);
2198
- if (!markerMatchesExpectedIdentity(marker, expected)) {
2107
+ const marker = parseInvocationMarkerIdentity(entries[markerIndex]?.data);
2108
+ if (marker === undefined) {
2199
2109
  return {
2200
2110
  disposition: "unavailable",
2201
2111
  source: "unknown",
@@ -2218,7 +2128,7 @@ export function extractNavigatorFact(
2218
2128
  !navigatorAttendanceCorrelatedWithBoundMarker(
2219
2129
  details,
2220
2130
  i,
2221
- { index: terminal.index, role: terminal.role },
2131
+ terminal.index,
2222
2132
  marker,
2223
2133
  )
2224
2134
  ) {
@@ -2249,7 +2159,7 @@ async function extractNavigatorFactFromAdmittedSession(
2249
2159
  ): Promise<TerminalNavigatorFact> {
2250
2160
  try {
2251
2161
  const entries = await readBoundSessionEntries(admitted.sessionFile);
2252
- return extractNavigatorFact(entries, attendanceIdentityFromAdmitted(admitted));
2162
+ return extractNavigatorFact(entries);
2253
2163
  } catch (error) {
2254
2164
  if (isMissingPathError(error)) {
2255
2165
  return {
@@ -2483,10 +2393,7 @@ async function settleLawfulJudgeTerminalResult(
2483
2393
  if (roleOutcome === undefined) {
2484
2394
  return undefined;
2485
2395
  }
2486
- const navigator = extractNavigatorFact(
2487
- entries,
2488
- attendanceIdentityFromAdmitted(admitted),
2489
- );
2396
+ const navigator = extractNavigatorFact(entries);
2490
2397
  // Lawful outcome exists — artifact publication keeps original errno/name.
2491
2398
  const artifacts = await publishJudgeArtifacts(
2492
2399
  admitted,
@@ -2539,10 +2446,7 @@ async function settleLawfulCoderTerminalResult(
2539
2446
  if (entries === undefined) return undefined;
2540
2447
  const extracted = extractCoderRoleOutcome(entries);
2541
2448
  if (extracted === undefined) return undefined;
2542
- const navigator = extractNavigatorFact(
2543
- entries,
2544
- attendanceIdentityFromAdmitted(admitted),
2545
- );
2449
+ const navigator = extractNavigatorFact(entries);
2546
2450
  const artifacts = await publishCoderArtifacts(
2547
2451
  admitted,
2548
2452
  extracted.outcome,
@@ -2745,10 +2649,7 @@ async function settleLawfulFixerTerminalResult(
2745
2649
  if (entries === undefined) return undefined;
2746
2650
  const extracted = extractFixerRoleOutcome(entries);
2747
2651
  if (extracted === undefined) return undefined;
2748
- const navigator = extractNavigatorFact(
2749
- entries,
2750
- attendanceIdentityFromAdmitted(admitted),
2751
- );
2652
+ const navigator = extractNavigatorFact(entries);
2752
2653
  const methodInvocations = extractFixerMethodInvocations(entries, {
2753
2654
  allowedLocations: [
2754
2655
  options.methodSkillPath,
@@ -2915,10 +2816,7 @@ async function settleLawfulCollectorTerminalResult(
2915
2816
  return undefined;
2916
2817
  }
2917
2818
  assertCollectorReceiptMatchesAdmitted(extracted.receipt, admitted);
2918
- const navigator = extractNavigatorFact(
2919
- entries,
2920
- attendanceIdentityFromAdmitted(admitted),
2921
- );
2819
+ const navigator = extractNavigatorFact(entries);
2922
2820
  const artifacts = await publishCollectorArtifacts(
2923
2821
  admitted,
2924
2822
  extracted.outcome,
@@ -3095,10 +2993,7 @@ async function settleLawfulDoctorTerminalResult(
3095
2993
  throw error;
3096
2994
  }
3097
2995
  }
3098
- const navigator = extractNavigatorFact(
3099
- entries,
3100
- attendanceIdentityFromAdmitted(admitted),
3101
- );
2996
+ const navigator = extractNavigatorFact(entries);
3102
2997
  const artifacts = await publishDoctorArtifacts(
3103
2998
  admitted,
3104
2999
  extracted.outcome,
@@ -3353,10 +3248,7 @@ async function settleLawfulReviewerTerminalResult(
3353
3248
  if (entries === undefined) return undefined;
3354
3249
  const extracted = extractReviewerRoleOutcome(entries);
3355
3250
  if (extracted === undefined) return undefined;
3356
- const navigator = extractNavigatorFact(
3357
- entries,
3358
- attendanceIdentityFromAdmitted(admitted),
3359
- );
3251
+ const navigator = extractNavigatorFact(entries);
3360
3252
  const methodInvocations = extractReviewerMethodInvocations(entries, {
3361
3253
  allowedLocations: [
3362
3254
  options.methodSkillPath,
@@ -3623,10 +3515,7 @@ async function settleLawfulMergerTerminalResult(
3623
3515
  });
3624
3516
  // Every invocation must expand the merge-only method before conflict work.
3625
3517
  if (methodInvocations.length === 0) return undefined;
3626
- const navigator = extractNavigatorFact(
3627
- entries,
3628
- attendanceIdentityFromAdmitted(admitted),
3629
- );
3518
+ const navigator = extractNavigatorFact(entries);
3630
3519
  const artifacts = await publishMergerArtifacts(
3631
3520
  admitted,
3632
3521
  extracted.outcome,
@@ -23,7 +23,6 @@ import {
23
23
  } from "./tool-execution-observation.ts";
24
24
  import { registerEngineDetourTool } from "./engine-detour-tool.ts";
25
25
  import { installPackageOwnedToolRegistration } from "./package-owned-tool-idle.ts";
26
- import { installWorkerGitHooks } from "./worker-submission-gates.ts";
27
26
  import { createReceiptDeliveryPolicy, NO_RECEIPT_LIFECYCLE_ENTRY_TYPE, RECEIPT_DELIVERY_PROMPT } from "./receipt-delivery-policy.ts";
28
27
  import { createOAuthKeepalive, type OAuthKeepaliveOptions } from "./oauth-keepalive.ts";
29
28
 
@@ -1177,10 +1176,9 @@ export function createRoleRuntimeExtension(
1177
1176
  engineDetourRegistration = undefined;
1178
1177
  }
1179
1178
  }
1180
- // Worker gates ②④ + ① baseline: envelope arms the worktree after role install.
1179
+ // Worker gates ①②: arm records baseline and runs private one-shot hook uninstall (ADR 0070).
1181
1180
  // Parent session feeds #216 createRecordSession so baseline/bounce survive resume.
1182
1181
  if (entry.role === "coder" || entry.role === "fixer") {
1183
- installWorkerGitHooks(ctx.cwd);
1184
1182
  if (entry.role === "coder") coder.armSubmissionGate(ctx.cwd, ctx.sessionManager);
1185
1183
  else fixer.armSubmissionGate(ctx.cwd, ctx.sessionManager);
1186
1184
  }
@@ -29,6 +29,7 @@ import {
29
29
  import {
30
30
  createWorkerSubmissionGate,
31
31
  WorkerCommitReminderError,
32
+ WorkerPrefixReminderError,
32
33
  WorkerUnfinishedReasonReminderError,
33
34
  } from "./worker-submission-gates.ts";
34
35
 
@@ -189,6 +190,7 @@ function assertAcceptableThroughHost(
189
190
  } catch (error) {
190
191
  if (
191
192
  error instanceof WorkerCommitReminderError ||
193
+ error instanceof WorkerPrefixReminderError ||
192
194
  error instanceof WorkerUnfinishedReasonReminderError
193
195
  ) {
194
196
  throw error;