@sun-asterisk/sungen 3.3.0-alpha.3 → 3.3.0-alpha.5

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.
@@ -1,5 +1,17 @@
1
1
  /** stdout of a git command run in `cwd`, or null when git fails (no repo, no HEAD, untracked path…). */
2
2
  export declare function gitOutput(cwd: string, args: string[]): string | null;
3
+ /**
4
+ * The text of each `<rev>:<path>` spec, read by ONE `git cat-file --batch` in `cwd` (`./path` is relative
5
+ * to it): null where git holds no such file; null for the whole call when git fails.
6
+ */
7
+ export declare function gitFileTexts(cwd: string, specs: string[]): Array<string | null> | null;
8
+ /**
9
+ * Whether `cwd` is in a shallow clone. git < 2.15 does not know `--is-shallow-repository` and echoes it
10
+ * back, so only an exact `true` / `false` is an answer; otherwise the `shallow` file in the git directory
11
+ * (common dir, or the git dir) decides, and when not even that can be found the clone is taken as shallow
12
+ * — its history is then unknown, which fails closed for the callers that ask.
13
+ */
14
+ export declare function isShallowRepository(cwd: string): boolean;
3
15
  /** The committed text of `abs` (a file inside `projectRoot`), or null when git HEAD has no such file. */
4
16
  export declare function committedText(projectRoot: string, abs: string): string | null;
5
17
  /** The committed JSON of `abs`, or null (absent from HEAD, or not JSON). */
@@ -1 +1 @@
1
- {"version":3,"file":"git-head-trust.d.ts","sourceRoot":"","sources":["../../src/decisions/git-head-trust.ts"],"names":[],"mappings":"AAUA,wGAAwG;AACxG,wBAAgB,SAAS,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,MAAM,GAAG,IAAI,CAMpE;AAED,yGAAyG;AACzG,wBAAgB,aAAa,CAAC,WAAW,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAI7E;AAED,4EAA4E;AAC5E,wBAAgB,aAAa,CAAC,WAAW,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAIvE"}
1
+ {"version":3,"file":"git-head-trust.d.ts","sourceRoot":"","sources":["../../src/decisions/git-head-trust.ts"],"names":[],"mappings":"AAWA,wGAAwG;AACxG,wBAAgB,SAAS,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,MAAM,GAAG,IAAI,CAMpE;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,KAAK,CAAC,MAAM,GAAG,IAAI,CAAC,GAAG,IAAI,CAuBtF;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAMxD;AAED,yGAAyG;AACzG,wBAAgB,aAAa,CAAC,WAAW,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAI7E;AAED,4EAA4E;AAC5E,wBAAgB,aAAa,CAAC,WAAW,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAIvE"}
@@ -34,6 +34,8 @@ var __importStar = (this && this.__importStar) || (function () {
34
34
  })();
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
36
  exports.gitOutput = gitOutput;
37
+ exports.gitFileTexts = gitFileTexts;
38
+ exports.isShallowRepository = isShallowRepository;
37
39
  exports.committedText = committedText;
38
40
  exports.committedJson = committedJson;
39
41
  /**
@@ -44,6 +46,7 @@ exports.committedJson = committedJson;
44
46
  * did not. Outside git, or for a file git does not track, nothing is trusted. Never throws.
45
47
  */
46
48
  const child_process_1 = require("child_process");
49
+ const fs = __importStar(require("fs"));
47
50
  const path = __importStar(require("path"));
48
51
  /** stdout of a git command run in `cwd`, or null when git fails (no repo, no HEAD, untracked path…). */
49
52
  function gitOutput(cwd, args) {
@@ -54,6 +57,55 @@ function gitOutput(cwd, args) {
54
57
  return null;
55
58
  }
56
59
  }
60
+ /**
61
+ * The text of each `<rev>:<path>` spec, read by ONE `git cat-file --batch` in `cwd` (`./path` is relative
62
+ * to it): null where git holds no such file; null for the whole call when git fails.
63
+ */
64
+ function gitFileTexts(cwd, specs) {
65
+ if (specs.length === 0)
66
+ return [];
67
+ if (specs.some((s) => /[\r\n]/.test(s)))
68
+ return null;
69
+ let out;
70
+ try {
71
+ out = (0, child_process_1.execFileSync)('git', ['cat-file', '--batch'], { cwd, input: `${specs.join('\n')}\n`, stdio: ['pipe', 'pipe', 'ignore'], maxBuffer: 256 * 1024 * 1024, timeout: 60000 });
72
+ }
73
+ catch {
74
+ return null;
75
+ }
76
+ // Each answer: `<oid> <type> <size>\n<content>\n`, or `<spec> missing\n` (also `ambiguous`).
77
+ const texts = [];
78
+ let at = 0;
79
+ for (let i = 0; i < specs.length; i++) {
80
+ const eol = out.indexOf(0x0a, at);
81
+ if (eol < 0)
82
+ return null;
83
+ const header = out.subarray(at, eol).toString('utf8').split(' ');
84
+ at = eol + 1;
85
+ if (header.length !== 3 || !/^\d+$/.test(header[2])) {
86
+ texts.push(null);
87
+ continue;
88
+ }
89
+ const size = Number(header[2]);
90
+ texts.push(header[1] === 'blob' ? out.subarray(at, at + size).toString('utf8') : null);
91
+ at += size + 1;
92
+ }
93
+ return texts;
94
+ }
95
+ /**
96
+ * Whether `cwd` is in a shallow clone. git < 2.15 does not know `--is-shallow-repository` and echoes it
97
+ * back, so only an exact `true` / `false` is an answer; otherwise the `shallow` file in the git directory
98
+ * (common dir, or the git dir) decides, and when not even that can be found the clone is taken as shallow
99
+ * — its history is then unknown, which fails closed for the callers that ask.
100
+ */
101
+ function isShallowRepository(cwd) {
102
+ const answer = gitOutput(cwd, ['rev-parse', '--is-shallow-repository'])?.trim();
103
+ if (answer === 'true' || answer === 'false')
104
+ return answer === 'true';
105
+ const dirs = ['--git-common-dir', '--git-dir'].map((flag) => gitOutput(cwd, ['rev-parse', flag])?.trim())
106
+ .filter((d) => !!d && !d.startsWith('-'));
107
+ return dirs.length === 0 || dirs.some((d) => fs.existsSync(path.join(path.resolve(cwd, d), 'shallow')));
108
+ }
57
109
  /** The committed text of `abs` (a file inside `projectRoot`), or null when git HEAD has no such file. */
58
110
  function committedText(projectRoot, abs) {
59
111
  const rel = path.relative(projectRoot, abs).split(path.sep).join('/');
@@ -1 +1 @@
1
- {"version":3,"file":"git-head-trust.js","sourceRoot":"","sources":["../../src/decisions/git-head-trust.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAWA,8BAMC;AAGD,sCAIC;AAGD,sCAIC;AA/BD;;;;;;GAMG;AACH,iDAA6C;AAC7C,2CAA6B;AAE7B,wGAAwG;AACxG,SAAgB,SAAS,CAAC,GAAW,EAAE,IAAc;IACnD,IAAI,CAAC;QACH,OAAO,IAAA,4BAAY,EAAC,KAAK,EAAE,IAAI,EAAE,EAAE,GAAG,EAAE,KAAK,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,SAAS,EAAE,EAAE,GAAG,IAAI,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;IAC/I,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,yGAAyG;AACzG,SAAgB,aAAa,CAAC,WAAmB,EAAE,GAAW;IAC5D,MAAM,GAAG,GAAG,IAAI,CAAC,QAAQ,CAAC,WAAW,EAAE,GAAG,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACtE,IAAI,CAAC,GAAG,IAAI,GAAG,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC;QAAE,OAAO,IAAI,CAAC;IACtE,OAAO,SAAS,CAAC,WAAW,EAAE,CAAC,MAAM,EAAE,UAAU,GAAG,EAAE,CAAC,CAAC,CAAC;AAC3D,CAAC;AAED,4EAA4E;AAC5E,SAAgB,aAAa,CAAC,WAAmB,EAAE,GAAW;IAC5D,MAAM,IAAI,GAAG,aAAa,CAAC,WAAW,EAAE,GAAG,CAAC,CAAC;IAC7C,IAAI,IAAI,KAAK,IAAI;QAAE,OAAO,IAAI,CAAC;IAC/B,IAAI,CAAC;QAAC,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,CAAC;IAAC,CAAC;IAAC,MAAM,CAAC;QAAC,OAAO,IAAI,CAAC;IAAC,CAAC;AAC3E,CAAC"}
1
+ {"version":3,"file":"git-head-trust.js","sourceRoot":"","sources":["../../src/decisions/git-head-trust.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAYA,8BAMC;AAMD,oCAuBC;AAQD,kDAMC;AAGD,sCAIC;AAGD,sCAIC;AA3ED;;;;;;GAMG;AACH,iDAA6C;AAC7C,uCAAyB;AACzB,2CAA6B;AAE7B,wGAAwG;AACxG,SAAgB,SAAS,CAAC,GAAW,EAAE,IAAc;IACnD,IAAI,CAAC;QACH,OAAO,IAAA,4BAAY,EAAC,KAAK,EAAE,IAAI,EAAE,EAAE,GAAG,EAAE,KAAK,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,SAAS,EAAE,EAAE,GAAG,IAAI,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;IAC/I,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,SAAgB,YAAY,CAAC,GAAW,EAAE,KAAe;IACvD,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC;IAClC,IAAI,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAAE,OAAO,IAAI,CAAC;IACrD,IAAI,GAAW,CAAC;IAChB,IAAI,CAAC;QACH,GAAG,GAAG,IAAA,4BAAY,EAAC,KAAK,EAAE,CAAC,UAAU,EAAE,SAAS,CAAC,EAAE,EAAE,GAAG,EAAE,KAAK,EAAE,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,KAAK,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,SAAS,EAAE,GAAG,GAAG,IAAI,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC,CAAC;IAC/K,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;IACD,6FAA6F;IAC7F,MAAM,KAAK,GAAyB,EAAE,CAAC;IACvC,IAAI,EAAE,GAAG,CAAC,CAAC;IACX,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACtC,MAAM,GAAG,GAAG,GAAG,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;QAClC,IAAI,GAAG,GAAG,CAAC;YAAE,OAAO,IAAI,CAAC;QACzB,MAAM,MAAM,GAAG,GAAG,CAAC,QAAQ,CAAC,EAAE,EAAE,GAAG,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QACjE,EAAE,GAAG,GAAG,GAAG,CAAC,CAAC;QACb,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;YAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAAC,SAAS;QAAC,CAAC;QACpF,MAAM,IAAI,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;QAC/B,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE,EAAE,EAAE,GAAG,IAAI,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;QACvF,EAAE,IAAI,IAAI,GAAG,CAAC,CAAC;IACjB,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;;;;GAKG;AACH,SAAgB,mBAAmB,CAAC,GAAW;IAC7C,MAAM,MAAM,GAAG,SAAS,CAAC,GAAG,EAAE,CAAC,WAAW,EAAE,yBAAyB,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC;IAChF,IAAI,MAAM,KAAK,MAAM,IAAI,MAAM,KAAK,OAAO;QAAE,OAAO,MAAM,KAAK,MAAM,CAAC;IACtE,MAAM,IAAI,GAAG,CAAC,kBAAkB,EAAE,WAAW,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,SAAS,CAAC,GAAG,EAAE,CAAC,WAAW,EAAE,IAAI,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC;SACtG,MAAM,CAAC,CAAC,CAAC,EAAe,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC;IACzD,OAAO,IAAI,CAAC,MAAM,KAAK,CAAC,IAAI,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC,EAAE,SAAS,CAAC,CAAC,CAAC,CAAC;AAC1G,CAAC;AAED,yGAAyG;AACzG,SAAgB,aAAa,CAAC,WAAmB,EAAE,GAAW;IAC5D,MAAM,GAAG,GAAG,IAAI,CAAC,QAAQ,CAAC,WAAW,EAAE,GAAG,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACtE,IAAI,CAAC,GAAG,IAAI,GAAG,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC;QAAE,OAAO,IAAI,CAAC;IACtE,OAAO,SAAS,CAAC,WAAW,EAAE,CAAC,MAAM,EAAE,UAAU,GAAG,EAAE,CAAC,CAAC,CAAC;AAC3D,CAAC;AAED,4EAA4E;AAC5E,SAAgB,aAAa,CAAC,WAAmB,EAAE,GAAW;IAC5D,MAAM,IAAI,GAAG,aAAa,CAAC,WAAW,EAAE,GAAG,CAAC,CAAC;IAC7C,IAAI,IAAI,KAAK,IAAI;QAAE,OAAO,IAAI,CAAC;IAC/B,IAAI,CAAC;QAAC,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,CAAC;IAAC,CAAC;IAAC,MAAM,CAAC;QAAC,OAAO,IAAI,CAAC;IAAC,CAAC;AAC3E,CAAC"}
@@ -26,6 +26,11 @@ export declare function untrustedWaiverText(unit: string, slug: string, title: s
26
26
  * verify, a migration in progress or interrupted — is raised, never swallowed.
27
27
  */
28
28
  export declare function commitWaiverSync(projectRoot: string, unit: string, slug: string, plan: WaiverSyncPlan, statePath: string, stateText: string): boolean;
29
- /** Record a waiver given now on the CLI; supersedes the waiver in force for that obligation, if any. */
29
+ /**
30
+ * Record a waiver given now on the CLI; supersedes the waiver in force for that obligation, if any.
31
+ * The same waiver again records nothing — unless the one in force was imported (`where: migrated`):
32
+ * re-confirming it on a terminal is how a human vouches for a migrated waiver its legacy record cannot
33
+ * (decision.in-run), so it always writes the `cli` decision that supersedes it (#925).
34
+ */
30
35
  export declare function recordWaiver(projectRoot: string, unit: string, obligation: string, w: LegacyWaiver): AppendResult;
31
36
  //# sourceMappingURL=legacy-waiver-migration.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"legacy-waiver-migration.d.ts","sourceRoot":"","sources":["../../src/decisions/legacy-waiver-migration.ts"],"names":[],"mappings":"AAqBA,OAAO,EAAkB,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAGjE,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAKlD,OAAO,EAA0D,YAAY,EAA2B,MAAM,oBAAoB,CAAC;AAEnI,MAAM,WAAW,eAAe;IAAG,UAAU,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,YAAY,CAAA;CAAE;AAE7E,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,0EAA0E;IAC1E,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;IACtC,SAAS,EAAE,eAAe,EAAE,CAAC;IAC7B,yGAAyG;IACzG,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACtC,OAAO,EAAE,aAAa,EAAE,CAAC;IACzB,8EAA8E;IAC9E,QAAQ,EAAE,OAAO,CAAC;CACnB;AAED,eAAO,MAAM,gBAAgB,GAAI,aAAa,MAAM,EAAE,MAAM,MAAM,KAAG,MACa,CAAC;AAQnF,wBAAgB,cAAc,CAAC,WAAW,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,GAAG,cAAc,CA0BlI;AAED,yGAAyG;AACzG,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,EAAE,eAAe,GAAG,MAAM,CAIzG;AASD;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,WAAW,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAwBrJ;AAED,wGAAwG;AACxG,wBAAgB,YAAY,CAAC,WAAW,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,CAAC,EAAE,YAAY,GAAG,YAAY,CAMjH"}
1
+ {"version":3,"file":"legacy-waiver-migration.d.ts","sourceRoot":"","sources":["../../src/decisions/legacy-waiver-migration.ts"],"names":[],"mappings":"AAqBA,OAAO,EAAkB,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAGjE,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAKlD,OAAO,EAA0D,YAAY,EAA2B,MAAM,oBAAoB,CAAC;AAEnI,MAAM,WAAW,eAAe;IAAG,UAAU,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,YAAY,CAAA;CAAE;AAE7E,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,0EAA0E;IAC1E,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;IACtC,SAAS,EAAE,eAAe,EAAE,CAAC;IAC7B,yGAAyG;IACzG,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACtC,OAAO,EAAE,aAAa,EAAE,CAAC;IACzB,8EAA8E;IAC9E,QAAQ,EAAE,OAAO,CAAC;CACnB;AAED,eAAO,MAAM,gBAAgB,GAAI,aAAa,MAAM,EAAE,MAAM,MAAM,KAAG,MACa,CAAC;AAQnF,wBAAgB,cAAc,CAAC,WAAW,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,GAAG,cAAc,CA0BlI;AAED,yGAAyG;AACzG,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,EAAE,eAAe,GAAG,MAAM,CAIzG;AASD;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,WAAW,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAwBrJ;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,WAAW,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,CAAC,EAAE,YAAY,GAAG,YAAY,CAQjH"}
@@ -157,14 +157,20 @@ function commitWaiverSync(projectRoot, unit, slug, plan, statePath, stateText) {
157
157
  throw err;
158
158
  }
159
159
  }
160
- /** Record a waiver given now on the CLI; supersedes the waiver in force for that obligation, if any. */
160
+ /**
161
+ * Record a waiver given now on the CLI; supersedes the waiver in force for that obligation, if any.
162
+ * The same waiver again records nothing — unless the one in force was imported (`where: migrated`):
163
+ * re-confirming it on a terminal is how a human vouches for a migrated waiver its legacy record cannot
164
+ * (decision.in-run), so it always writes the `cli` decision that supersedes it (#925).
165
+ */
161
166
  function recordWaiver(projectRoot, unit, obligation, w) {
162
167
  const scope = (0, decision_file_1.unitDecisionScope)(projectRoot, unit);
163
168
  if (!scope)
164
169
  throw new Error(`Cannot record a waiver: "${unit}" does not name exactly one unit under qa/screens, qa/flows or qa/api.`);
165
170
  const current = (0, waiver_decisions_1.effectiveWaiverEntries)((0, read_decisions_1.readDecisions)(projectRoot, scope, { kind: 'waiver' }).entries).get(obligation);
166
- if (current && (0, waiver_decisions_1.sameWaiver)((0, waiver_decisions_1.asLegacyWaiver)(current), w))
171
+ if (current && current.source?.where !== 'migrated' && (0, waiver_decisions_1.sameWaiver)((0, waiver_decisions_1.asLegacyWaiver)(current), w)) {
167
172
  return { status: 'already-recorded', entry: current, path: current.path };
173
+ }
168
174
  return (0, append_decision_1.appendDecision)(projectRoot, scope, (0, waiver_decisions_1.waiverDraft)(obligation, w, { by: (0, decision_source_1.gitUserName)(projectRoot), where: 'cli' }, current?.id));
169
175
  }
170
176
  //# sourceMappingURL=legacy-waiver-migration.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"legacy-waiver-migration.js","sourceRoot":"","sources":["../../src/decisions/legacy-waiver-migration.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"legacy-waiver-migration.js","sourceRoot":"","sources":["../../src/decisions/legacy-waiver-migration.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"in-run-decision-check.d.ts","sourceRoot":"","sources":["../../../src/harness/gates/in-run-decision-check.ts"],"names":[],"mappings":"AAqBA,OAAO,EAAE,kBAAkB,EAAE,MAAM,iCAAiC,CAAC;AAKrE,eAAO,MAAM,YAAY,2DAA4D,CAAC;AACtF,eAAO,MAAM,WAAW,oBAAoB,CAAC;AAE7C,MAAM,WAAW,aAAa;IAAG,EAAE,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,kBAAkB,CAAC;IAAC,GAAG,EAAE,MAAM,EAAE,CAAA;CAAE;AAExH,sGAAsG;AACtG,wBAAgB,sBAAsB,CAAC,WAAW,EAAE,MAAM,GAAG,GAAG,CAAC,MAAM,CAAC,GAAG,IAAI,CAI9E;AAQD,0GAA0G;AAC1G,wBAAgB,UAAU,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,GAAG,IAAI,CASpE;AAID,wHAAwH;AACxH,wBAAgB,cAAc,CAAC,WAAW,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI,EAAE,SAAS,EAAE,GAAG,CAAC,MAAM,CAAC,GAAG,IAAI,GAAG,aAAa,EAAE,CA6B7I;AAED,2HAA2H;AAC3H,wBAAgB,2BAA2B,CAAC,WAAW,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,aAAa,EAAE,CAElG;AAED,0GAA0G;AAC1G,wBAAgB,qBAAqB,CAAC,WAAW,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,OAAO,CAEpF"}
1
+ {"version":3,"file":"in-run-decision-check.d.ts","sourceRoot":"","sources":["../../../src/harness/gates/in-run-decision-check.ts"],"names":[],"mappings":"AAwBA,OAAO,EAAE,kBAAkB,EAAE,MAAM,iCAAiC,CAAC;AAKrE,eAAO,MAAM,YAAY,2DAA4D,CAAC;AACtF,eAAO,MAAM,WAAW,oBAAoB,CAAC;AAE7C,MAAM,WAAW,aAAa;IAAG,EAAE,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,kBAAkB,CAAC;IAAC,GAAG,EAAE,MAAM,EAAE,CAAA;CAAE;AAExH,sGAAsG;AACtG,wBAAgB,sBAAsB,CAAC,WAAW,EAAE,MAAM,GAAG,GAAG,CAAC,MAAM,CAAC,GAAG,IAAI,CAI9E;AAQD,0GAA0G;AAC1G,wBAAgB,UAAU,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,GAAG,IAAI,CASpE;AAID,wHAAwH;AACxH,wBAAgB,cAAc,CAAC,WAAW,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI,EAAE,SAAS,EAAE,GAAG,CAAC,MAAM,CAAC,GAAG,IAAI,GAAG,aAAa,EAAE,CA8B7I;AAED,2HAA2H;AAC3H,wBAAgB,2BAA2B,CAAC,WAAW,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,aAAa,EAAE,CAElG;AAED,0GAA0G;AAC1G,wBAAgB,qBAAqB,CAAC,WAAW,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,OAAO,CAEpF"}
@@ -52,10 +52,13 @@ exports.hasInRunKindDecisions = hasInRunKindDecisions;
52
52
  *
53
53
  * A MIGRATED waiver (DOS-13.5) was reviewed when its legacy record was committed — the only record the
54
54
  * import trusts — so it is judged by THAT commit (migrated-waiver-review.ts), never by its own decision
55
- * file: in-run when the record was committed after the run started, or, with no run to compare,
56
- * after the decision file appeared. Holding it to its decision file's commit would make every legacy
57
- * project's waivers in-run the day they are imported (phase 09). An entry HEAD's legacy file does not
58
- * vouch for — absent, uncommitted, different — is judged as any other.
55
+ * file: in-run when the record was committed after the run started, or — run start or not (#925) —
56
+ * when it could not have been the import's source: not strictly before the decision file, the unit's
57
+ * import marker or the project's adoption of 3.3 appeared. Holding it to its decision file's commit would make
58
+ * every legacy project's waivers in-run the day they are imported (phase 09). An entry HEAD's legacy
59
+ * file does not vouch for — absent, uncommitted, different, introduced beyond the commits the walk
60
+ * reads (#924) or at a shallow clone's boundary — is judged as any other; in the last two cases an
61
+ * in-run finding says why.
59
62
  */
60
63
  const fs = __importStar(require("fs"));
61
64
  const path = __importStar(require("path"));
@@ -95,19 +98,22 @@ const isInRunKind = (kind) => exports.IN_RUN_KINDS.includes(kind);
95
98
  function inRunDecisions(projectRoot, scopes, runStart, committed) {
96
99
  const out = [];
97
100
  const after = (t) => !!runStart && Date.parse(t) > Date.parse(runStart);
101
+ const migratedReview = committed ? (0, migrated_waiver_review_1.migratedWaiverReview)(projectRoot) : null;
98
102
  for (const scope of scopes) {
99
103
  for (const e of (0, read_decisions_1.readDecisions)(projectRoot, scope).entries) {
100
104
  if (!isInRunKind(e.kind))
101
105
  continue;
106
+ // A migrated waiver re-confirmed on a terminal is replaced: the new decision is judged on its own.
107
+ if (e.superseded_by && e.source?.where === 'migrated')
108
+ continue;
102
109
  const rel = path.relative(projectRoot, e.path).split(path.sep).join('/');
103
110
  const why = [];
104
- const legacyAt = committed ? (0, migrated_waiver_review_1.migratedWaiverReviewedAt)(projectRoot, scope, e) : null;
105
- if (legacyAt) {
106
- if (runStart ? after(legacyAt) : Date.parse(legacyAt) > (0, migrated_waiver_review_1.decisionFirstSeenMs)(projectRoot, rel, e.path)) {
107
- why.push(runStart
108
- ? `migrated: its legacy record was committed at ${legacyAt}, after the run started ${runStart}`
109
- : `migrated: its legacy record was committed at ${legacyAt}, after this decision first appeared`);
110
- }
111
+ const migrated = migratedReview?.reviewOf(scope, e) ?? null;
112
+ if (migrated?.at) {
113
+ if (after(migrated.at))
114
+ why.push(`migrated: its legacy record was committed at ${migrated.at}, after the run started ${runStart}`);
115
+ if (migrated.notImported)
116
+ why.push(migrated.notImported);
111
117
  }
112
118
  else if (committed && !committed.has(rel)) {
113
119
  why.push('not in git HEAD (never committed through review)');
@@ -121,6 +127,8 @@ function inRunDecisions(projectRoot, scopes, runStart, committed) {
121
127
  else if (!committed && after(e.valid_from)) {
122
128
  why.push(`recorded ${e.valid_from}, after the run started ${runStart}`);
123
129
  }
130
+ if (why.length && migrated?.unvouched)
131
+ why.push(migrated.unvouched);
124
132
  if (why.length)
125
133
  out.push({ id: e.id, kind: e.kind, path: rel, visibility: e.visibility, why });
126
134
  }
@@ -1 +1 @@
1
- {"version":3,"file":"in-run-decision-check.js","sourceRoot":"","sources":["../../../src/harness/gates/in-run-decision-check.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAgCA,wDAIC;AASD,gCASC;AAKD,wCA6BC;AAGD,kEAEC;AAGD,sDAEC;AAlGD;;;;;;;;;;;;;;;;;GAiBG;AACH,uCAAyB;AACzB,2CAA6B;AAC7B,iEAAmE;AAEnE,mEAA2D;AAC3D,mEAA+D;AAC/D,qEAAyF;AAE5E,QAAA,YAAY,GAAG,CAAC,eAAe,EAAE,QAAQ,EAAE,mBAAmB,CAAU,CAAC;AACzE,QAAA,WAAW,GAAG,iBAAiB,CAAC;AAI7C,sGAAsG;AACtG,SAAgB,sBAAsB,CAAC,WAAmB;IACxD,IAAI,IAAA,0BAAS,EAAC,WAAW,EAAE,CAAC,WAAW,EAAE,uBAAuB,CAAC,CAAC,EAAE,IAAI,EAAE,KAAK,MAAM;QAAE,OAAO,IAAI,CAAC;IACnG,MAAM,MAAM,GAAG,IAAA,0BAAS,EAAC,WAAW,EAAE,CAAC,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,aAAa,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC,IAAI,EAAE,CAAC,CAAG,kCAAkC;IAC7I,OAAO,IAAI,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,kCAAkB,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AAC/E,CAAC;AAED,mFAAmF;AACnF,SAAS,YAAY,CAAC,WAAmB,EAAE,GAAW;IACpD,MAAM,CAAC,GAAG,IAAA,0BAAS,EAAC,WAAW,EAAE,CAAC,KAAK,EAAE,IAAI,EAAE,cAAc,EAAE,IAAI,EAAE,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC;IACnF,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;AACtD,CAAC;AAED,0GAA0G;AAC1G,SAAgB,UAAU,CAAC,WAA0B;IACnD,IAAI,CAAC,WAAW;QAAE,OAAO,IAAI,CAAC;IAC9B,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,YAAY,CAAC,WAAW,EAAE,MAAM,CAAC,CAAwC,CAAC;QACrG,MAAM,CAAC,GAAG,IAAI,EAAE,KAAK,EAAE,SAAS,CAAC;QACjC,OAAO,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IAC1E,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,MAAM,WAAW,GAAG,CAAC,IAAY,EAAW,EAAE,CAAE,oBAAkC,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;AAElG,wHAAwH;AACxH,SAAgB,cAAc,CAAC,WAAmB,EAAE,MAAgB,EAAE,QAAuB,EAAE,SAA6B;IAC1H,MAAM,GAAG,GAAoB,EAAE,CAAC;IAChC,MAAM,KAAK,GAAG,CAAC,CAAS,EAAW,EAAE,CAAC,CAAC,CAAC,QAAQ,IAAI,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;IACzF,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QAC3B,KAAK,MAAM,CAAC,IAAI,IAAA,8BAAa,EAAC,WAAW,EAAE,KAAK,CAAC,CAAC,OAAO,EAAE,CAAC;YAC1D,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC,IAAI,CAAC;gBAAE,SAAS;YACnC,MAAM,GAAG,GAAG,IAAI,CAAC,QAAQ,CAAC,WAAW,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YACzE,MAAM,GAAG,GAAa,EAAE,CAAC;YACzB,MAAM,QAAQ,GAAG,SAAS,CAAC,CAAC,CAAC,IAAA,iDAAwB,EAAC,WAAW,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;YACpF,IAAI,QAAQ,EAAE,CAAC;gBACb,IAAI,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,GAAG,IAAA,4CAAmB,EAAC,WAAW,EAAE,GAAG,EAAE,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC;oBACtG,GAAG,CAAC,IAAI,CAAC,QAAQ;wBACf,CAAC,CAAC,gDAAgD,QAAQ,2BAA2B,QAAQ,EAAE;wBAC/F,CAAC,CAAC,gDAAgD,QAAQ,sCAAsC,CAAC,CAAC;gBACtG,CAAC;YACH,CAAC;iBAAM,IAAI,SAAS,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC;gBAC5C,GAAG,CAAC,IAAI,CAAC,kDAAkD,CAAC,CAAC;YAC/D,CAAC;iBAAM,IAAI,SAAS,IAAI,QAAQ,EAAE,CAAC;gBACjC,MAAM,EAAE,GAAG,YAAY,CAAC,WAAW,EAAE,GAAG,CAAC,CAAC;gBAC1C,IAAI,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,UAAU,CAAC,EAAE,CAAC;oBACzC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,gBAAgB,EAAE,2BAA2B,QAAQ,EAAE,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,UAAU,qDAAqD,QAAQ,EAAE,CAAC,CAAC;gBACnK,CAAC;YACH,CAAC;iBAAM,IAAI,CAAC,SAAS,IAAI,KAAK,CAAC,CAAC,CAAC,UAAU,CAAC,EAAE,CAAC;gBAC7C,GAAG,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC,UAAU,2BAA2B,QAAQ,EAAE,CAAC,CAAC;YAC1E,CAAC;YACD,IAAI,GAAG,CAAC,MAAM;gBAAE,GAAG,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,IAAI,EAAE,GAAG,EAAE,UAAU,EAAE,CAAC,CAAC,UAAU,EAAE,GAAG,EAAE,CAAC,CAAC;QACjG,CAAC;IACH,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,2HAA2H;AAC3H,SAAgB,2BAA2B,CAAC,WAAmB,EAAE,MAAgB;IAC/E,OAAO,cAAc,CAAC,WAAW,EAAE,MAAM,EAAE,IAAI,EAAE,sBAAsB,CAAC,WAAW,CAAC,CAAC,CAAC;AACxF,CAAC;AAED,0GAA0G;AAC1G,SAAgB,qBAAqB,CAAC,WAAmB,EAAE,MAAgB;IACzE,OAAO,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAA,8BAAa,EAAC,WAAW,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,WAAW,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AACpG,CAAC"}
1
+ {"version":3,"file":"in-run-decision-check.js","sourceRoot":"","sources":["../../../src/harness/gates/in-run-decision-check.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ import type { ReadDecision } from '../../decisions/read-decisions';
2
+ /**
3
+ * What a human does about a migrated waiver its legacy record cannot vouch for: record it anew on a
4
+ * terminal (a `cli` decision that supersedes it — the superseded one is no longer judged), or accept it
5
+ * for one delivery.
6
+ */
7
+ export declare const migratedWaiverRemedy: (scope: string, ob: string) => string;
8
+ /** One run's bounds: each decision's first appearance, each unit's marker and the project's adoption, read once. */
9
+ export declare function migratedWaiverImportBounds(projectRoot: string): {
10
+ /** Why a record committed at `at` (state file of `slug`) cannot have been the source of `e`'s import; undefined when it can. */
11
+ notImported(slug: string, e: ReadDecision, at: string): string | undefined;
12
+ };
13
+ //# sourceMappingURL=migrated-waiver-import-bounds.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"migrated-waiver-import-bounds.d.ts","sourceRoot":"","sources":["../../../src/harness/gates/migrated-waiver-import-bounds.ts"],"names":[],"mappings":"AAyBA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gCAAgC,CAAC;AAKnE;;;;GAIG;AACH,eAAO,MAAM,oBAAoB,GAAI,OAAO,MAAM,EAAE,IAAI,MAAM,KAAG,MAE2B,CAAC;AAO7F,oHAAoH;AACpH,wBAAgB,0BAA0B,CAAC,WAAW,EAAE,MAAM;IAmD1D,gIAAgI;sBAC9G,MAAM,KAAK,YAAY,MAAM,MAAM,GAAG,MAAM,GAAG,SAAS;EAK7E"}
@@ -0,0 +1,141 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.migratedWaiverRemedy = void 0;
37
+ exports.migratedWaiverImportBounds = migratedWaiverImportBounds;
38
+ /**
39
+ * Could a migrated waiver's legacy record have been the source of the import that wrote it? (#925)
40
+ *
41
+ * The one-time import (legacy-waiver-migration.ts) reads a record git HEAD ALREADY holds, and only
42
+ * before 3.3 takes over. So a genuine record was committed strictly before each of:
43
+ * 1. the decision file's first appearance — its first commit, else its creation on disk;
44
+ * 2. the unit's import marker `.sungen/migrations/<slug>.waivers-imported.json` — the commit that
45
+ * added the one HEAD holds (an agent can delete or recreate the file, not that commit), else its
46
+ * creation on disk; once it exists the import never reads that unit's legacy file again;
47
+ * 3. the project's adoption of 3.3 — the first commit that added any 3.3-only file: an import marker
48
+ * or other `.sungen/migrations/` file, a decision file (`qa/**\/decisions/*.yaml`) or a contract lock
49
+ * (`qa/**\/contracts.lock.yaml`). Only the 3.3 CLI writes them, and only committed ones count: a
50
+ * legacy waiver is one written before the project's first reviewed 3.3 change. Mixed versions pay
51
+ * for it: a waiver a pre-3.3 CLI writes after that commit must be re-confirmed on a terminal.
52
+ * A record failing any bound claims a migration that never happened: the decision is in-run.
53
+ *
54
+ * What stays open (the limit of any time-based check, as for ordinary decisions): a record forged and
55
+ * committed before the project's first 3.3 commit — indistinguishable from a real legacy waiver, as on
56
+ * a project adopting 3.3 for the very first time — and commit times forged with GIT_COMMITTER_DATE.
57
+ */
58
+ const fs = __importStar(require("fs"));
59
+ const path = __importStar(require("path"));
60
+ const git_head_trust_1 = require("../../decisions/git-head-trust");
61
+ const human_confirmation_1 = require("../../decisions/human-confirmation");
62
+ const legacy_waiver_migration_1 = require("../../decisions/legacy-waiver-migration");
63
+ /** Files only the 3.3 CLI writes, as pathspecs relative to the project. */
64
+ const ADOPTION_PATHSPECS = ['.sungen/migrations', ':(glob)qa/**/decisions/*.yaml', ':(glob)qa/**/contracts.lock.yaml'];
65
+ /**
66
+ * What a human does about a migrated waiver its legacy record cannot vouch for: record it anew on a
67
+ * terminal (a `cli` decision that supersedes it — the superseded one is no longer judged), or accept it
68
+ * for one delivery.
69
+ */
70
+ const migratedWaiverRemedy = (scope, ob) => `re-confirm it on a terminal: ${(0, human_confirmation_1.formatCommand)(['sungen', 'journey', '--screen', scope.split('/').pop() ?? scope, '--waive', ob, '--reason', '…'])}, `
71
+ + 'then commit the new decision; or accept it once with `sungen delivery --accept-in-run`';
72
+ exports.migratedWaiverRemedy = migratedWaiverRemedy;
73
+ const validMs = (iso) => (iso && !Number.isNaN(Date.parse(iso)) ? Date.parse(iso) : null);
74
+ const createdMs = (abs) => {
75
+ try {
76
+ const s = fs.statSync(abs);
77
+ return s.birthtimeMs || s.mtimeMs;
78
+ }
79
+ catch {
80
+ return null;
81
+ }
82
+ };
83
+ /** One run's bounds: each decision's first appearance, each unit's marker and the project's adoption, read once. */
84
+ function migratedWaiverImportBounds(projectRoot) {
85
+ const relOf = (abs) => path.relative(projectRoot, abs).split(path.sep).join('/');
86
+ const firstSeen = new Map();
87
+ const markers = new Map();
88
+ let adoption;
89
+ /** When a decision file first appeared: its first commit, else its creation (or last change) on disk. */
90
+ const decisionMs = (e) => {
91
+ if (!firstSeen.has(e.path)) {
92
+ const added = (0, git_head_trust_1.gitOutput)(projectRoot, ['log', '--diff-filter=A', '--format=%cI', '--', relOf(e.path)])?.trim().split('\n').filter(Boolean).pop();
93
+ firstSeen.set(e.path, validMs(added) ?? createdMs(e.path) ?? Number.NEGATIVE_INFINITY);
94
+ }
95
+ return firstSeen.get(e.path);
96
+ };
97
+ const markerMs = (slug) => {
98
+ if (!markers.has(slug)) {
99
+ const abs = (0, legacy_waiver_migration_1.importMarkerPath)(projectRoot, slug);
100
+ const added = (0, git_head_trust_1.committedText)(projectRoot, abs) !== null
101
+ ? validMs((0, git_head_trust_1.gitOutput)(projectRoot, ['log', '-1', '--diff-filter=A', '--format=%cI', '--', relOf(abs)])?.trim()) : null;
102
+ markers.set(slug, added ?? createdMs(abs));
103
+ }
104
+ return markers.get(slug);
105
+ };
106
+ const adoptionMs = () => {
107
+ if (adoption === undefined) {
108
+ const added = ((0, git_head_trust_1.gitOutput)(projectRoot, ['log', '--diff-filter=A', '--format=%cI', '--', ...ADOPTION_PATHSPECS]) ?? '')
109
+ .split('\n').map((l) => validMs(l.trim())).filter((t) => t !== null);
110
+ adoption = added.length ? Math.min(...added) : null;
111
+ }
112
+ return adoption;
113
+ };
114
+ /** The first bound a record committed at `legacy` (ms) crosses, or undefined. */
115
+ const boundCrossed = (slug, e, legacy) => {
116
+ const decision = decisionMs(e);
117
+ if (legacy > decision)
118
+ return 'after this decision first appeared';
119
+ if (legacy === decision)
120
+ return 'when this decision first appeared, not before it — an import only reads a record already committed';
121
+ const marker = markerMs(slug);
122
+ if (marker !== null && legacy >= marker) {
123
+ return `after this unit's waivers were imported (${relOf((0, legacy_waiver_migration_1.importMarkerPath)(projectRoot, slug))}, ${new Date(marker).toISOString()}) `
124
+ + '— from then on the import never reads the legacy file';
125
+ }
126
+ const adopted = adoptionMs();
127
+ if (adopted !== null && legacy >= adopted) {
128
+ return `after this project adopted sungen 3.3 (${new Date(adopted).toISOString()}: the first commit adding a .sungen/migrations/ file, `
129
+ + 'a decision or a contract lock) — a legacy waiver is one written before that';
130
+ }
131
+ return undefined;
132
+ };
133
+ return {
134
+ /** Why a record committed at `at` (state file of `slug`) cannot have been the source of `e`'s import; undefined when it can. */
135
+ notImported(slug, e, at) {
136
+ const why = boundCrossed(slug, e, Date.parse(at));
137
+ return why && `migrated: its legacy record was committed at ${at}, ${why}; ${(0, exports.migratedWaiverRemedy)(e.scope, String(e.payload?.obligation))}`;
138
+ },
139
+ };
140
+ }
141
+ //# sourceMappingURL=migrated-waiver-import-bounds.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"migrated-waiver-import-bounds.js","sourceRoot":"","sources":["../../../src/harness/gates/migrated-waiver-import-bounds.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA6CA,gEAyDC;AAtGD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,uCAAyB;AACzB,2CAA6B;AAC7B,mEAA0E;AAC1E,2EAAmE;AACnE,qFAA2E;AAG3E,2EAA2E;AAC3E,MAAM,kBAAkB,GAAG,CAAC,oBAAoB,EAAE,+BAA+B,EAAE,kCAAkC,CAAC,CAAC;AAEvH;;;;GAIG;AACI,MAAM,oBAAoB,GAAG,CAAC,KAAa,EAAE,EAAU,EAAU,EAAE,CACxE,gCAAgC,IAAA,kCAAa,EAAC,CAAC,QAAQ,EAAE,SAAS,EAAE,UAAU,EAAE,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,IAAI,KAAK,EAAE,SAAS,EAAE,EAAE,EAAE,UAAU,EAAE,GAAG,CAAC,CAAC,IAAI;MACnJ,wFAAwF,CAAC;AAFhF,QAAA,oBAAoB,wBAE4D;AAE7F,MAAM,OAAO,GAAG,CAAC,GAAuB,EAAiB,EAAE,CAAC,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;AAC7H,MAAM,SAAS,GAAG,CAAC,GAAW,EAAiB,EAAE;IAC/C,IAAI,CAAC;QAAC,MAAM,CAAC,GAAG,EAAE,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;QAAC,OAAO,CAAC,CAAC,WAAW,IAAI,CAAC,CAAC,OAAO,CAAC;IAAC,CAAC;IAAC,MAAM,CAAC;QAAC,OAAO,IAAI,CAAC;IAAC,CAAC;AAC/F,CAAC,CAAC;AAEF,oHAAoH;AACpH,SAAgB,0BAA0B,CAAC,WAAmB;IAC5D,MAAM,KAAK,GAAG,CAAC,GAAW,EAAU,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,WAAW,EAAE,GAAG,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACjG,MAAM,SAAS,GAAG,IAAI,GAAG,EAAkB,CAAC;IAC5C,MAAM,OAAO,GAAG,IAAI,GAAG,EAAyB,CAAC;IACjD,IAAI,QAAmC,CAAC;IAExC,yGAAyG;IACzG,MAAM,UAAU,GAAG,CAAC,CAAe,EAAU,EAAE;QAC7C,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC;YAC3B,MAAM,KAAK,GAAG,IAAA,0BAAS,EAAC,WAAW,EAAE,CAAC,KAAK,EAAE,iBAAiB,EAAE,cAAc,EAAE,IAAI,EAAE,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,GAAG,EAAE,CAAC;YAChJ,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,OAAO,CAAC,KAAK,CAAC,IAAI,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,MAAM,CAAC,iBAAiB,CAAC,CAAC;QACzF,CAAC;QACD,OAAO,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAE,CAAC;IAChC,CAAC,CAAC;IACF,MAAM,QAAQ,GAAG,CAAC,IAAY,EAAiB,EAAE;QAC/C,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC;YACvB,MAAM,GAAG,GAAG,IAAA,0CAAgB,EAAC,WAAW,EAAE,IAAI,CAAC,CAAC;YAChD,MAAM,KAAK,GAAG,IAAA,8BAAa,EAAC,WAAW,EAAE,GAAG,CAAC,KAAK,IAAI;gBACpD,CAAC,CAAC,OAAO,CAAC,IAAA,0BAAS,EAAC,WAAW,EAAE,CAAC,KAAK,EAAE,IAAI,EAAE,iBAAiB,EAAE,cAAc,EAAE,IAAI,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;YACvH,OAAO,CAAC,GAAG,CAAC,IAAI,EAAE,KAAK,IAAI,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC;QAC7C,CAAC;QACD,OAAO,OAAO,CAAC,GAAG,CAAC,IAAI,CAAE,CAAC;IAC5B,CAAC,CAAC;IACF,MAAM,UAAU,GAAG,GAAkB,EAAE;QACrC,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;YAC3B,MAAM,KAAK,GAAG,CAAC,IAAA,0BAAS,EAAC,WAAW,EAAE,CAAC,KAAK,EAAE,iBAAiB,EAAE,cAAc,EAAE,IAAI,EAAE,GAAG,kBAAkB,CAAC,CAAC,IAAI,EAAE,CAAC;iBAClH,KAAK,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAe,EAAE,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC;YACpF,QAAQ,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;QACtD,CAAC;QACD,OAAO,QAAQ,CAAC;IAClB,CAAC,CAAC;IAEF,iFAAiF;IACjF,MAAM,YAAY,GAAG,CAAC,IAAY,EAAE,CAAe,EAAE,MAAc,EAAsB,EAAE;QACzF,MAAM,QAAQ,GAAG,UAAU,CAAC,CAAC,CAAC,CAAC;QAC/B,IAAI,MAAM,GAAG,QAAQ;YAAE,OAAO,oCAAoC,CAAC;QACnE,IAAI,MAAM,KAAK,QAAQ;YAAE,OAAO,oGAAoG,CAAC;QACrI,MAAM,MAAM,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC;QAC9B,IAAI,MAAM,KAAK,IAAI,IAAI,MAAM,IAAI,MAAM,EAAE,CAAC;YACxC,OAAO,4CAA4C,KAAK,CAAC,IAAA,0CAAgB,EAAC,WAAW,EAAE,IAAI,CAAC,CAAC,KAAK,IAAI,IAAI,CAAC,MAAM,CAAC,CAAC,WAAW,EAAE,IAAI;kBAChI,uDAAuD,CAAC;QAC9D,CAAC;QACD,MAAM,OAAO,GAAG,UAAU,EAAE,CAAC;QAC7B,IAAI,OAAO,KAAK,IAAI,IAAI,MAAM,IAAI,OAAO,EAAE,CAAC;YAC1C,OAAO,0CAA0C,IAAI,IAAI,CAAC,OAAO,CAAC,CAAC,WAAW,EAAE,wDAAwD;kBACpI,6EAA6E,CAAC;QACpF,CAAC;QACD,OAAO,SAAS,CAAC;IACnB,CAAC,CAAC;IAEF,OAAO;QACL,gIAAgI;QAChI,WAAW,CAAC,IAAY,EAAE,CAAe,EAAE,EAAU;YACnD,MAAM,GAAG,GAAG,YAAY,CAAC,IAAI,EAAE,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC;YAClD,OAAO,GAAG,IAAI,gDAAgD,EAAE,KAAK,GAAG,KAAK,IAAA,4BAAoB,EAAC,CAAC,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC,CAAC,OAAO,EAAE,UAAU,CAAC,CAAC,EAAE,CAAC;QAC9I,CAAC;KACF,CAAC;AACJ,CAAC"}
@@ -1,6 +1,16 @@
1
1
  import type { ReadDecision } from '../../decisions/read-decisions';
2
- /** When a migrated waiver's legacy record was committed, or null when nothing in git HEAD vouches for it. */
3
- export declare function migratedWaiverReviewedAt(projectRoot: string, scope: string, e: ReadDecision): string | null;
4
- /** When a decision file first appeared: its first commit, else its creation (or last change) on disk. */
5
- export declare function decisionFirstSeenMs(projectRoot: string, rel: string, abs: string): number;
2
+ /** A migrated waiver HEAD's legacy file holds: when it was reviewed, or why that cannot be said. */
3
+ export interface MigratedReview {
4
+ /** When its legacy record was committed; null when that commit is out of reach (see `unvouched`). */
5
+ at: string | null;
6
+ /** Set with `at`: why the record cannot have been the import's source — the decision is in-run. */
7
+ notImported?: string;
8
+ /** Set when `at` is null: why the record vouches for nothing — the decision is judged as any other. */
9
+ unvouched?: string;
10
+ }
11
+ /** One run's reads for migrated waivers: each state file's history, decision's first commit and import marker, read once. */
12
+ export declare function migratedWaiverReview(projectRoot: string): {
13
+ /** How a migrated waiver was reviewed; null when it is not one HEAD's legacy file holds (judged as any other). */
14
+ reviewOf(scope: string, e: ReadDecision): MigratedReview | null;
15
+ };
6
16
  //# sourceMappingURL=migrated-waiver-review.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"migrated-waiver-review.d.ts","sourceRoot":"","sources":["../../../src/harness/gates/migrated-waiver-review.ts"],"names":[],"mappings":"AAkBA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gCAAgC,CAAC;AA6BnE,6GAA6G;AAC7G,wBAAgB,wBAAwB,CAAC,WAAW,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,EAAE,YAAY,GAAG,MAAM,GAAG,IAAI,CAY3G;AAED,yGAAyG;AACzG,wBAAgB,mBAAmB,CAAC,WAAW,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAIzF"}
1
+ {"version":3,"file":"migrated-waiver-review.d.ts","sourceRoot":"","sources":["../../../src/harness/gates/migrated-waiver-review.ts"],"names":[],"mappings":"AA2BA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gCAAgC,CAAC;AAYnE,oGAAoG;AACpG,MAAM,WAAW,cAAc;IAC7B,qGAAqG;IACrG,EAAE,EAAE,MAAM,GAAG,IAAI,CAAC;IAClB,mGAAmG;IACnG,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,uGAAuG;IACvG,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AA4CD,6HAA6H;AAC7H,wBAAgB,oBAAoB,CAAC,WAAW,EAAE,MAAM;IAWpD,kHAAkH;oBAClG,MAAM,KAAK,YAAY,GAAG,cAAc,GAAG,IAAI;EAgBlE"}
@@ -1,40 +1,6 @@
1
1
  "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
- Object.defineProperty(o, "default", { enumerable: true, value: v });
15
- }) : function(o, v) {
16
- o["default"] = v;
17
- });
18
- var __importStar = (this && this.__importStar) || (function () {
19
- var ownKeys = function(o) {
20
- ownKeys = Object.getOwnPropertyNames || function (o) {
21
- var ar = [];
22
- for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
- return ar;
24
- };
25
- return ownKeys(o);
26
- };
27
- return function (mod) {
28
- if (mod && mod.__esModule) return mod;
29
- var result = {};
30
- if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
- __setModuleDefault(result, mod);
32
- return result;
33
- };
34
- })();
35
2
  Object.defineProperty(exports, "__esModule", { value: true });
36
- exports.migratedWaiverReviewedAt = migratedWaiverReviewedAt;
37
- exports.decisionFirstSeenMs = decisionFirstSeenMs;
3
+ exports.migratedWaiverReview = migratedWaiverReview;
38
4
  /**
39
5
  * When a MIGRATED waiver was reviewed (DOS-13.5, phase 09) — for `decision.in-run`.
40
6
  *
@@ -44,18 +10,28 @@ exports.decisionFirstSeenMs = decisionFirstSeenMs;
44
10
  *
45
11
  * vouched HEAD's state file holds the record (same reason, `at`, auditHashAtWaive) — reviewedAt is
46
12
  * the commit time of the oldest commit in the unbroken run, back from HEAD, that holds it
47
- * with a run start in-run when reviewedAt is after it (the same test as any committed decision)
48
- * without one in-run when reviewedAt is after the decision file first appeared (its first
49
- * commit, else its creation time): a genuine import always follows the commit it read
13
+ * imported the record could have been the import's source (#925) — always checked, run start or not:
14
+ * committed strictly before the decision file, the unit's import marker and the project's
15
+ * adoption of 3.3 first appeared (migrated-waiver-import-bounds.ts). Failing one, the decision
16
+ * claims a migration that never happened → in-run, even at `gate --phase deliver`, which has
17
+ * no run start.
18
+ * with a run start also in-run when reviewedAt is after it (the same test as any committed decision)
50
19
  * Anything else — no git, no such record in HEAD, a record that differs — is not vouched for: the
51
- * decision is judged as any other (`not in git HEAD`, or its own commit time).
20
+ * decision is judged as any other (`not in git HEAD`, or its own commit time). Only a rewritten history
21
+ * (a squash or rebase that folds the record's commit into the import's) can fail a genuine import; the
22
+ * finding says which bound it crossed.
23
+ *
24
+ * Nor is a record whose introducing commit is out of reach — judged as any other, with `unvouched`
25
+ * saying why, never vouched for by a later commit taken for its introduction: the walk reads at most
26
+ * MAX_COMMITS commits of a state file and the record is held by the commit before them too (#924), or
27
+ * the record reaches a shallow clone's boundary commit. One review reads each state file's history
28
+ * once (one `git log`, one `git cat-file --batch`), however many waivers share it.
52
29
  */
53
- const fs = __importStar(require("fs"));
54
- const path = __importStar(require("path"));
55
30
  const git_head_trust_1 = require("../../decisions/git-head-trust");
56
31
  const waiver_decisions_1 = require("../../decisions/waiver-decisions");
57
32
  const unit_paths_1 = require("../unit-paths");
58
- /** Commits of one state file the walk reads at most (each is one `git show`). */
33
+ const migrated_waiver_import_bounds_1 = require("./migrated-waiver-import-bounds");
34
+ /** Commits of one state file the walk reads at most. */
59
35
  const MAX_COMMITS = 200;
60
36
  const parse = (text) => {
61
37
  if (text === null)
@@ -71,46 +47,68 @@ function holds(state, e, ob) {
71
47
  const w = state?.waivers?.[ob];
72
48
  return (0, waiver_decisions_1.isLegacyWaiver)(w) && w.at === e.valid_from && (0, waiver_decisions_1.sameWaiver)(w, (0, waiver_decisions_1.asLegacyWaiver)(e));
73
49
  }
74
- /** The commit time of the oldest commit, in the unbroken run back from HEAD, whose state file holds the record. */
75
- function introducedAt(projectRoot, rel, e, ob) {
76
- const log = (0, git_head_trust_1.gitOutput)(projectRoot, ['log', `--max-count=${MAX_COMMITS}`, '--format=%H %cI', '--', rel]) ?? '';
77
- let oldest = null;
78
- for (const line of log.split('\n').filter(Boolean)) {
79
- const [sha, at] = line.split(' ');
80
- if (!holds(parse((0, git_head_trust_1.gitOutput)(projectRoot, ['show', `${sha}:./${rel}`])), e, ob))
81
- break;
82
- oldest = at;
83
- }
84
- return oldest && !Number.isNaN(Date.parse(oldest)) ? oldest : null;
85
- }
86
- /** When a migrated waiver's legacy record was committed, or null when nothing in git HEAD vouches for it. */
87
- function migratedWaiverReviewedAt(projectRoot, scope, e) {
88
- const ob = e.payload?.obligation;
89
- if (e.kind !== 'waiver' || e.source?.where !== 'migrated' || typeof ob !== 'string')
90
- return null;
91
- // The journey keys its state by the unit name it was given: `<name>`, or the catalog id for api units.
92
- const slugs = new Set([scope.split('/').pop() ?? '', (0, unit_paths_1.reportSlug)(scope.replace(/^(screens|flows)\//, ''))]);
93
- for (const slug of slugs) {
94
- const file = path.join(projectRoot, '.sungen', 'journey', `${slug}.state.json`);
95
- if (!holds(parse((0, git_head_trust_1.committedText)(projectRoot, file)), e, ob))
96
- continue;
97
- const at = introducedAt(projectRoot, path.relative(projectRoot, file).split(path.sep).join('/'), e, ob);
98
- if (at)
99
- return at;
100
- }
101
- return null;
50
+ function readHistory(projectRoot, rel) {
51
+ const log = ((0, git_head_trust_1.gitOutput)(projectRoot, ['log', `--max-count=${MAX_COMMITS + 1}`, '--format=%H %cI %P', '--', rel]) ?? '')
52
+ .split('\n').filter(Boolean).map((line) => line.trim().split(' '));
53
+ const texts = (0, git_head_trust_1.gitFileTexts)(projectRoot, [`HEAD:./${rel}`, ...log.map(([sha]) => `${sha}:./${rel}`)]);
54
+ return {
55
+ rel,
56
+ head: parse(texts?.[0] ?? null),
57
+ commits: log.map(([, at, ...parents], i) => ({ at, state: parse(texts?.[i + 1] ?? null), root: parents.length === 0 })),
58
+ };
102
59
  }
103
- /** When a decision file first appeared: its first commit, else its creation (or last change) on disk. */
104
- function decisionFirstSeenMs(projectRoot, rel, abs) {
105
- const added = (0, git_head_trust_1.gitOutput)(projectRoot, ['log', '--diff-filter=A', '--format=%cI', '--', rel])?.trim().split('\n').filter(Boolean).pop();
106
- if (added && !Number.isNaN(Date.parse(added)))
107
- return Date.parse(added);
108
- try {
109
- const s = fs.statSync(abs);
110
- return s.birthtimeMs || s.mtimeMs;
60
+ /**
61
+ * The commit time of the oldest commit, in the unbroken run back from HEAD, whose state file holds the
62
+ * record. The walk reads MAX_COMMITS commits; the next one only says whether the last of them introduced it.
63
+ * In a shallow clone a commit with no parent shown is the boundary: every file seems to appear there, so a
64
+ * record reaching it has no known commit time.
65
+ */
66
+ function introducedAt(h, e, ob, scope, shallow) {
67
+ let run = 0;
68
+ while (run < h.commits.length && holds(h.commits[run].state, e, ob))
69
+ run++;
70
+ const unvouched = (why) => ({ at: null, unvouched: `migrated, but its legacy record cannot vouch for it: ${why}` });
71
+ if (run > MAX_COMMITS) {
72
+ return unvouched(`${h.rel} changed in more than ${MAX_COMMITS} commits, and the commit that introduced the record is older than all of them`);
111
73
  }
112
- catch {
113
- return Number.NEGATIVE_INFINITY;
74
+ const oldest = run ? h.commits[run - 1] : null;
75
+ if (oldest?.root && shallow()) {
76
+ return unvouched(`the git history is shallow, so the record's real commit time is unknown — fetch the full history `
77
+ + `(\`git fetch --unshallow\`; \`fetch-depth: 0\` in CI), or ${(0, migrated_waiver_import_bounds_1.migratedWaiverRemedy)(scope, ob)}`);
114
78
  }
79
+ return oldest && !Number.isNaN(Date.parse(oldest.at)) ? { at: oldest.at } : null;
80
+ }
81
+ /** One run's reads for migrated waivers: each state file's history, decision's first commit and import marker, read once. */
82
+ function migratedWaiverReview(projectRoot) {
83
+ const histories = new Map();
84
+ const bounds = (0, migrated_waiver_import_bounds_1.migratedWaiverImportBounds)(projectRoot);
85
+ let isShallow;
86
+ const shallow = () => (isShallow ?? (isShallow = (0, git_head_trust_1.isShallowRepository)(projectRoot)));
87
+ const historyOf = (rel) => {
88
+ if (!histories.has(rel))
89
+ histories.set(rel, readHistory(projectRoot, rel));
90
+ return histories.get(rel);
91
+ };
92
+ return {
93
+ /** How a migrated waiver was reviewed; null when it is not one HEAD's legacy file holds (judged as any other). */
94
+ reviewOf(scope, e) {
95
+ const ob = e.payload?.obligation;
96
+ if (e.kind !== 'waiver' || e.source?.where !== 'migrated' || typeof ob !== 'string')
97
+ return null;
98
+ // The journey keys its state by the unit name it was given: `<name>`, or the catalog id for api units.
99
+ const slugs = new Set([scope.split('/').pop() ?? '', (0, unit_paths_1.reportSlug)(scope.replace(/^(screens|flows)\//, ''))]);
100
+ let unvouched = null;
101
+ for (const slug of slugs) {
102
+ const h = historyOf(`.sungen/journey/${slug}.state.json`);
103
+ if (!holds(h.head, e, ob))
104
+ continue;
105
+ const review = introducedAt(h, e, ob, scope, shallow);
106
+ if (review?.at)
107
+ return { at: review.at, notImported: bounds.notImported(slug, e, review.at) };
108
+ unvouched = unvouched ?? review;
109
+ }
110
+ return unvouched;
111
+ },
112
+ };
115
113
  }
116
114
  //# sourceMappingURL=migrated-waiver-review.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"migrated-waiver-review.js","sourceRoot":"","sources":["../../../src/harness/gates/migrated-waiver-review.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"migrated-waiver-review.js","sourceRoot":"","sources":["../../../src/harness/gates/migrated-waiver-review.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sun-asterisk/sungen",
3
- "version": "3.3.0-alpha.3",
3
+ "version": "3.3.0-alpha.5",
4
4
  "description": "Deterministic E2E Test Compiler - Gherkin + Selectors → Playwright tests",
5
5
  "main": "src/index.ts",
6
6
  "types": "src/index.ts",
@@ -40,8 +40,8 @@
40
40
  "@babel/types": "^7.28.5",
41
41
  "@cucumber/gherkin": "^37.0.0",
42
42
  "@cucumber/messages": "^31.0.0",
43
- "@sungen/driver-data-factory": "3.3.0-alpha.3",
44
- "@sungen/driver-ui": "3.3.0-alpha.3",
43
+ "@sungen/driver-data-factory": "3.3.0-alpha.5",
44
+ "@sungen/driver-ui": "3.3.0-alpha.5",
45
45
  "chalk": "^5.6.2",
46
46
  "commander": "^14.0.2",
47
47
  "dotenv": "^17.2.3",
@@ -6,6 +6,7 @@
6
6
  * did not. Outside git, or for a file git does not track, nothing is trusted. Never throws.
7
7
  */
8
8
  import { execFileSync } from 'child_process';
9
+ import * as fs from 'fs';
9
10
  import * as path from 'path';
10
11
 
11
12
  /** stdout of a git command run in `cwd`, or null when git fails (no repo, no HEAD, untracked path…). */
@@ -17,6 +18,49 @@ export function gitOutput(cwd: string, args: string[]): string | null {
17
18
  }
18
19
  }
19
20
 
21
+ /**
22
+ * The text of each `<rev>:<path>` spec, read by ONE `git cat-file --batch` in `cwd` (`./path` is relative
23
+ * to it): null where git holds no such file; null for the whole call when git fails.
24
+ */
25
+ export function gitFileTexts(cwd: string, specs: string[]): Array<string | null> | null {
26
+ if (specs.length === 0) return [];
27
+ if (specs.some((s) => /[\r\n]/.test(s))) return null;
28
+ let out: Buffer;
29
+ try {
30
+ out = execFileSync('git', ['cat-file', '--batch'], { cwd, input: `${specs.join('\n')}\n`, stdio: ['pipe', 'pipe', 'ignore'], maxBuffer: 256 * 1024 * 1024, timeout: 60000 });
31
+ } catch {
32
+ return null;
33
+ }
34
+ // Each answer: `<oid> <type> <size>\n<content>\n`, or `<spec> missing\n` (also `ambiguous`).
35
+ const texts: Array<string | null> = [];
36
+ let at = 0;
37
+ for (let i = 0; i < specs.length; i++) {
38
+ const eol = out.indexOf(0x0a, at);
39
+ if (eol < 0) return null;
40
+ const header = out.subarray(at, eol).toString('utf8').split(' ');
41
+ at = eol + 1;
42
+ if (header.length !== 3 || !/^\d+$/.test(header[2])) { texts.push(null); continue; }
43
+ const size = Number(header[2]);
44
+ texts.push(header[1] === 'blob' ? out.subarray(at, at + size).toString('utf8') : null);
45
+ at += size + 1;
46
+ }
47
+ return texts;
48
+ }
49
+
50
+ /**
51
+ * Whether `cwd` is in a shallow clone. git < 2.15 does not know `--is-shallow-repository` and echoes it
52
+ * back, so only an exact `true` / `false` is an answer; otherwise the `shallow` file in the git directory
53
+ * (common dir, or the git dir) decides, and when not even that can be found the clone is taken as shallow
54
+ * — its history is then unknown, which fails closed for the callers that ask.
55
+ */
56
+ export function isShallowRepository(cwd: string): boolean {
57
+ const answer = gitOutput(cwd, ['rev-parse', '--is-shallow-repository'])?.trim();
58
+ if (answer === 'true' || answer === 'false') return answer === 'true';
59
+ const dirs = ['--git-common-dir', '--git-dir'].map((flag) => gitOutput(cwd, ['rev-parse', flag])?.trim())
60
+ .filter((d): d is string => !!d && !d.startsWith('-'));
61
+ return dirs.length === 0 || dirs.some((d) => fs.existsSync(path.join(path.resolve(cwd, d), 'shallow')));
62
+ }
63
+
20
64
  /** The committed text of `abs` (a file inside `projectRoot`), or null when git HEAD has no such file. */
21
65
  export function committedText(projectRoot: string, abs: string): string | null {
22
66
  const rel = path.relative(projectRoot, abs).split(path.sep).join('/');
@@ -125,11 +125,18 @@ export function commitWaiverSync(projectRoot: string, unit: string, slug: string
125
125
  }
126
126
  }
127
127
 
128
- /** Record a waiver given now on the CLI; supersedes the waiver in force for that obligation, if any. */
128
+ /**
129
+ * Record a waiver given now on the CLI; supersedes the waiver in force for that obligation, if any.
130
+ * The same waiver again records nothing — unless the one in force was imported (`where: migrated`):
131
+ * re-confirming it on a terminal is how a human vouches for a migrated waiver its legacy record cannot
132
+ * (decision.in-run), so it always writes the `cli` decision that supersedes it (#925).
133
+ */
129
134
  export function recordWaiver(projectRoot: string, unit: string, obligation: string, w: LegacyWaiver): AppendResult {
130
135
  const scope = unitDecisionScope(projectRoot, unit);
131
136
  if (!scope) throw new Error(`Cannot record a waiver: "${unit}" does not name exactly one unit under qa/screens, qa/flows or qa/api.`);
132
137
  const current = effectiveWaiverEntries(readDecisions(projectRoot, scope, { kind: 'waiver' }).entries).get(obligation);
133
- if (current && sameWaiver(asLegacyWaiver(current), w)) return { status: 'already-recorded', entry: current, path: current.path };
138
+ if (current && current.source?.where !== 'migrated' && sameWaiver(asLegacyWaiver(current), w)) {
139
+ return { status: 'already-recorded', entry: current, path: current.path };
140
+ }
134
141
  return appendDecision(projectRoot, scope, waiverDraft(obligation, w, { by: gitUserName(projectRoot), where: 'cli' }, current?.id));
135
142
  }
@@ -11,10 +11,13 @@
11
11
  *
12
12
  * A MIGRATED waiver (DOS-13.5) was reviewed when its legacy record was committed — the only record the
13
13
  * import trusts — so it is judged by THAT commit (migrated-waiver-review.ts), never by its own decision
14
- * file: in-run when the record was committed after the run started, or, with no run to compare,
15
- * after the decision file appeared. Holding it to its decision file's commit would make every legacy
16
- * project's waivers in-run the day they are imported (phase 09). An entry HEAD's legacy file does not
17
- * vouch for — absent, uncommitted, different — is judged as any other.
14
+ * file: in-run when the record was committed after the run started, or — run start or not (#925) —
15
+ * when it could not have been the import's source: not strictly before the decision file, the unit's
16
+ * import marker or the project's adoption of 3.3 appeared. Holding it to its decision file's commit would make
17
+ * every legacy project's waivers in-run the day they are imported (phase 09). An entry HEAD's legacy
18
+ * file does not vouch for — absent, uncommitted, different, introduced beyond the commits the walk
19
+ * reads (#924) or at a shallow clone's boundary — is judged as any other; in the last two cases an
20
+ * in-run finding says why.
18
21
  */
19
22
  import * as fs from 'fs';
20
23
  import * as path from 'path';
@@ -22,7 +25,7 @@ import { DECISION_FILE_PATH } from '../../decisions/decision-file';
22
25
  import { DecisionVisibility } from '../../decisions/decision-schema';
23
26
  import { gitOutput } from '../../decisions/git-head-trust';
24
27
  import { readDecisions } from '../../decisions/read-decisions';
25
- import { decisionFirstSeenMs, migratedWaiverReviewedAt } from './migrated-waiver-review';
28
+ import { migratedWaiverReview } from './migrated-waiver-review';
26
29
 
27
30
  export const IN_RUN_KINDS = ['gate-override', 'waiver', 'contract-approval'] as const;
28
31
  export const IN_RUN_RULE = 'decision.in-run';
@@ -60,18 +63,18 @@ const isInRunKind = (kind: string): boolean => (IN_RUN_KINDS as readonly string[
60
63
  export function inRunDecisions(projectRoot: string, scopes: string[], runStart: string | null, committed: Set<string> | null): InRunDecision[] {
61
64
  const out: InRunDecision[] = [];
62
65
  const after = (t: string): boolean => !!runStart && Date.parse(t) > Date.parse(runStart);
66
+ const migratedReview = committed ? migratedWaiverReview(projectRoot) : null;
63
67
  for (const scope of scopes) {
64
68
  for (const e of readDecisions(projectRoot, scope).entries) {
65
69
  if (!isInRunKind(e.kind)) continue;
70
+ // A migrated waiver re-confirmed on a terminal is replaced: the new decision is judged on its own.
71
+ if (e.superseded_by && e.source?.where === 'migrated') continue;
66
72
  const rel = path.relative(projectRoot, e.path).split(path.sep).join('/');
67
73
  const why: string[] = [];
68
- const legacyAt = committed ? migratedWaiverReviewedAt(projectRoot, scope, e) : null;
69
- if (legacyAt) {
70
- if (runStart ? after(legacyAt) : Date.parse(legacyAt) > decisionFirstSeenMs(projectRoot, rel, e.path)) {
71
- why.push(runStart
72
- ? `migrated: its legacy record was committed at ${legacyAt}, after the run started ${runStart}`
73
- : `migrated: its legacy record was committed at ${legacyAt}, after this decision first appeared`);
74
- }
74
+ const migrated = migratedReview?.reviewOf(scope, e) ?? null;
75
+ if (migrated?.at) {
76
+ if (after(migrated.at)) why.push(`migrated: its legacy record was committed at ${migrated.at}, after the run started ${runStart}`);
77
+ if (migrated.notImported) why.push(migrated.notImported);
75
78
  } else if (committed && !committed.has(rel)) {
76
79
  why.push('not in git HEAD (never committed through review)');
77
80
  } else if (committed && runStart) {
@@ -82,6 +85,7 @@ export function inRunDecisions(projectRoot: string, scopes: string[], runStart:
82
85
  } else if (!committed && after(e.valid_from)) {
83
86
  why.push(`recorded ${e.valid_from}, after the run started ${runStart}`);
84
87
  }
88
+ if (why.length && migrated?.unvouched) why.push(migrated.unvouched);
85
89
  if (why.length) out.push({ id: e.id, kind: e.kind, path: rel, visibility: e.visibility, why });
86
90
  }
87
91
  }
@@ -0,0 +1,103 @@
1
+ /**
2
+ * Could a migrated waiver's legacy record have been the source of the import that wrote it? (#925)
3
+ *
4
+ * The one-time import (legacy-waiver-migration.ts) reads a record git HEAD ALREADY holds, and only
5
+ * before 3.3 takes over. So a genuine record was committed strictly before each of:
6
+ * 1. the decision file's first appearance — its first commit, else its creation on disk;
7
+ * 2. the unit's import marker `.sungen/migrations/<slug>.waivers-imported.json` — the commit that
8
+ * added the one HEAD holds (an agent can delete or recreate the file, not that commit), else its
9
+ * creation on disk; once it exists the import never reads that unit's legacy file again;
10
+ * 3. the project's adoption of 3.3 — the first commit that added any 3.3-only file: an import marker
11
+ * or other `.sungen/migrations/` file, a decision file (`qa/**\/decisions/*.yaml`) or a contract lock
12
+ * (`qa/**\/contracts.lock.yaml`). Only the 3.3 CLI writes them, and only committed ones count: a
13
+ * legacy waiver is one written before the project's first reviewed 3.3 change. Mixed versions pay
14
+ * for it: a waiver a pre-3.3 CLI writes after that commit must be re-confirmed on a terminal.
15
+ * A record failing any bound claims a migration that never happened: the decision is in-run.
16
+ *
17
+ * What stays open (the limit of any time-based check, as for ordinary decisions): a record forged and
18
+ * committed before the project's first 3.3 commit — indistinguishable from a real legacy waiver, as on
19
+ * a project adopting 3.3 for the very first time — and commit times forged with GIT_COMMITTER_DATE.
20
+ */
21
+ import * as fs from 'fs';
22
+ import * as path from 'path';
23
+ import { committedText, gitOutput } from '../../decisions/git-head-trust';
24
+ import { formatCommand } from '../../decisions/human-confirmation';
25
+ import { importMarkerPath } from '../../decisions/legacy-waiver-migration';
26
+ import type { ReadDecision } from '../../decisions/read-decisions';
27
+
28
+ /** Files only the 3.3 CLI writes, as pathspecs relative to the project. */
29
+ const ADOPTION_PATHSPECS = ['.sungen/migrations', ':(glob)qa/**/decisions/*.yaml', ':(glob)qa/**/contracts.lock.yaml'];
30
+
31
+ /**
32
+ * What a human does about a migrated waiver its legacy record cannot vouch for: record it anew on a
33
+ * terminal (a `cli` decision that supersedes it — the superseded one is no longer judged), or accept it
34
+ * for one delivery.
35
+ */
36
+ export const migratedWaiverRemedy = (scope: string, ob: string): string =>
37
+ `re-confirm it on a terminal: ${formatCommand(['sungen', 'journey', '--screen', scope.split('/').pop() ?? scope, '--waive', ob, '--reason', '…'])}, `
38
+ + 'then commit the new decision; or accept it once with `sungen delivery --accept-in-run`';
39
+
40
+ const validMs = (iso: string | undefined): number | null => (iso && !Number.isNaN(Date.parse(iso)) ? Date.parse(iso) : null);
41
+ const createdMs = (abs: string): number | null => {
42
+ try { const s = fs.statSync(abs); return s.birthtimeMs || s.mtimeMs; } catch { return null; }
43
+ };
44
+
45
+ /** One run's bounds: each decision's first appearance, each unit's marker and the project's adoption, read once. */
46
+ export function migratedWaiverImportBounds(projectRoot: string) {
47
+ const relOf = (abs: string): string => path.relative(projectRoot, abs).split(path.sep).join('/');
48
+ const firstSeen = new Map<string, number>();
49
+ const markers = new Map<string, number | null>();
50
+ let adoption: number | null | undefined;
51
+
52
+ /** When a decision file first appeared: its first commit, else its creation (or last change) on disk. */
53
+ const decisionMs = (e: ReadDecision): number => {
54
+ if (!firstSeen.has(e.path)) {
55
+ const added = gitOutput(projectRoot, ['log', '--diff-filter=A', '--format=%cI', '--', relOf(e.path)])?.trim().split('\n').filter(Boolean).pop();
56
+ firstSeen.set(e.path, validMs(added) ?? createdMs(e.path) ?? Number.NEGATIVE_INFINITY);
57
+ }
58
+ return firstSeen.get(e.path)!;
59
+ };
60
+ const markerMs = (slug: string): number | null => {
61
+ if (!markers.has(slug)) {
62
+ const abs = importMarkerPath(projectRoot, slug);
63
+ const added = committedText(projectRoot, abs) !== null
64
+ ? validMs(gitOutput(projectRoot, ['log', '-1', '--diff-filter=A', '--format=%cI', '--', relOf(abs)])?.trim()) : null;
65
+ markers.set(slug, added ?? createdMs(abs));
66
+ }
67
+ return markers.get(slug)!;
68
+ };
69
+ const adoptionMs = (): number | null => {
70
+ if (adoption === undefined) {
71
+ const added = (gitOutput(projectRoot, ['log', '--diff-filter=A', '--format=%cI', '--', ...ADOPTION_PATHSPECS]) ?? '')
72
+ .split('\n').map((l) => validMs(l.trim())).filter((t): t is number => t !== null);
73
+ adoption = added.length ? Math.min(...added) : null;
74
+ }
75
+ return adoption;
76
+ };
77
+
78
+ /** The first bound a record committed at `legacy` (ms) crosses, or undefined. */
79
+ const boundCrossed = (slug: string, e: ReadDecision, legacy: number): string | undefined => {
80
+ const decision = decisionMs(e);
81
+ if (legacy > decision) return 'after this decision first appeared';
82
+ if (legacy === decision) return 'when this decision first appeared, not before it — an import only reads a record already committed';
83
+ const marker = markerMs(slug);
84
+ if (marker !== null && legacy >= marker) {
85
+ return `after this unit's waivers were imported (${relOf(importMarkerPath(projectRoot, slug))}, ${new Date(marker).toISOString()}) `
86
+ + '— from then on the import never reads the legacy file';
87
+ }
88
+ const adopted = adoptionMs();
89
+ if (adopted !== null && legacy >= adopted) {
90
+ return `after this project adopted sungen 3.3 (${new Date(adopted).toISOString()}: the first commit adding a .sungen/migrations/ file, `
91
+ + 'a decision or a contract lock) — a legacy waiver is one written before that';
92
+ }
93
+ return undefined;
94
+ };
95
+
96
+ return {
97
+ /** Why a record committed at `at` (state file of `slug`) cannot have been the source of `e`'s import; undefined when it can. */
98
+ notImported(slug: string, e: ReadDecision, at: string): string | undefined {
99
+ const why = boundCrossed(slug, e, Date.parse(at));
100
+ return why && `migrated: its legacy record was committed at ${at}, ${why}; ${migratedWaiverRemedy(e.scope, String(e.payload?.obligation))}`;
101
+ },
102
+ };
103
+ }
@@ -7,62 +7,115 @@
7
7
  *
8
8
  * vouched HEAD's state file holds the record (same reason, `at`, auditHashAtWaive) — reviewedAt is
9
9
  * the commit time of the oldest commit in the unbroken run, back from HEAD, that holds it
10
- * with a run start in-run when reviewedAt is after it (the same test as any committed decision)
11
- * without one in-run when reviewedAt is after the decision file first appeared (its first
12
- * commit, else its creation time): a genuine import always follows the commit it read
10
+ * imported the record could have been the import's source (#925) — always checked, run start or not:
11
+ * committed strictly before the decision file, the unit's import marker and the project's
12
+ * adoption of 3.3 first appeared (migrated-waiver-import-bounds.ts). Failing one, the decision
13
+ * claims a migration that never happened → in-run, even at `gate --phase deliver`, which has
14
+ * no run start.
15
+ * with a run start also in-run when reviewedAt is after it (the same test as any committed decision)
13
16
  * Anything else — no git, no such record in HEAD, a record that differs — is not vouched for: the
14
- * decision is judged as any other (`not in git HEAD`, or its own commit time).
17
+ * decision is judged as any other (`not in git HEAD`, or its own commit time). Only a rewritten history
18
+ * (a squash or rebase that folds the record's commit into the import's) can fail a genuine import; the
19
+ * finding says which bound it crossed.
20
+ *
21
+ * Nor is a record whose introducing commit is out of reach — judged as any other, with `unvouched`
22
+ * saying why, never vouched for by a later commit taken for its introduction: the walk reads at most
23
+ * MAX_COMMITS commits of a state file and the record is held by the commit before them too (#924), or
24
+ * the record reaches a shallow clone's boundary commit. One review reads each state file's history
25
+ * once (one `git log`, one `git cat-file --batch`), however many waivers share it.
15
26
  */
16
- import * as fs from 'fs';
17
- import * as path from 'path';
18
- import { committedText, gitOutput } from '../../decisions/git-head-trust';
27
+ import { gitFileTexts, gitOutput, isShallowRepository } from '../../decisions/git-head-trust';
19
28
  import type { ReadDecision } from '../../decisions/read-decisions';
20
29
  import { asLegacyWaiver, isLegacyWaiver, sameWaiver } from '../../decisions/waiver-decisions';
21
30
  import { reportSlug } from '../unit-paths';
31
+ import { migratedWaiverImportBounds, migratedWaiverRemedy } from './migrated-waiver-import-bounds';
22
32
 
23
- /** Commits of one state file the walk reads at most (each is one `git show`). */
33
+ /** Commits of one state file the walk reads at most. */
24
34
  const MAX_COMMITS = 200;
25
35
 
26
- const parse = (text: string | null): { waivers?: Record<string, unknown> } | null => {
36
+ type State = { waivers?: Record<string, unknown> } | null;
37
+ /** A state file in git: HEAD's version and its newest MAX_COMMITS + 1 commits (newest first; `root`: no parent shown). */
38
+ interface StateHistory { rel: string; head: State; commits: Array<{ at: string; state: State; root: boolean }> }
39
+
40
+ /** A migrated waiver HEAD's legacy file holds: when it was reviewed, or why that cannot be said. */
41
+ export interface MigratedReview {
42
+ /** When its legacy record was committed; null when that commit is out of reach (see `unvouched`). */
43
+ at: string | null;
44
+ /** Set with `at`: why the record cannot have been the import's source — the decision is in-run. */
45
+ notImported?: string;
46
+ /** Set when `at` is null: why the record vouches for nothing — the decision is judged as any other. */
47
+ unvouched?: string;
48
+ }
49
+
50
+ const parse = (text: string | null): State => {
27
51
  if (text === null) return null;
28
52
  try { return JSON.parse(text.replace(/^/, '')); } catch { return null; }
29
53
  };
30
54
 
31
- function holds(state: { waivers?: Record<string, unknown> } | null, e: ReadDecision, ob: string): boolean {
55
+ function holds(state: State, e: ReadDecision, ob: string): boolean {
32
56
  const w = state?.waivers?.[ob];
33
57
  return isLegacyWaiver(w) && w.at === e.valid_from && sameWaiver(w, asLegacyWaiver(e));
34
58
  }
35
59
 
36
- /** The commit time of the oldest commit, in the unbroken run back from HEAD, whose state file holds the record. */
37
- function introducedAt(projectRoot: string, rel: string, e: ReadDecision, ob: string): string | null {
38
- const log = gitOutput(projectRoot, ['log', `--max-count=${MAX_COMMITS}`, '--format=%H %cI', '--', rel]) ?? '';
39
- let oldest: string | null = null;
40
- for (const line of log.split('\n').filter(Boolean)) {
41
- const [sha, at] = line.split(' ');
42
- if (!holds(parse(gitOutput(projectRoot, ['show', `${sha}:./${rel}`])), e, ob)) break;
43
- oldest = at;
44
- }
45
- return oldest && !Number.isNaN(Date.parse(oldest)) ? oldest : null;
60
+ function readHistory(projectRoot: string, rel: string): StateHistory {
61
+ const log = (gitOutput(projectRoot, ['log', `--max-count=${MAX_COMMITS + 1}`, '--format=%H %cI %P', '--', rel]) ?? '')
62
+ .split('\n').filter(Boolean).map((line) => line.trim().split(' '));
63
+ const texts = gitFileTexts(projectRoot, [`HEAD:./${rel}`, ...log.map(([sha]) => `${sha}:./${rel}`)]);
64
+ return {
65
+ rel,
66
+ head: parse(texts?.[0] ?? null),
67
+ commits: log.map(([, at, ...parents], i) => ({ at, state: parse(texts?.[i + 1] ?? null), root: parents.length === 0 })),
68
+ };
46
69
  }
47
70
 
48
- /** When a migrated waiver's legacy record was committed, or null when nothing in git HEAD vouches for it. */
49
- export function migratedWaiverReviewedAt(projectRoot: string, scope: string, e: ReadDecision): string | null {
50
- const ob = e.payload?.obligation;
51
- if (e.kind !== 'waiver' || e.source?.where !== 'migrated' || typeof ob !== 'string') return null;
52
- // The journey keys its state by the unit name it was given: `<name>`, or the catalog id for api units.
53
- const slugs = new Set([scope.split('/').pop() ?? '', reportSlug(scope.replace(/^(screens|flows)\//, ''))]);
54
- for (const slug of slugs) {
55
- const file = path.join(projectRoot, '.sungen', 'journey', `${slug}.state.json`);
56
- if (!holds(parse(committedText(projectRoot, file)), e, ob)) continue;
57
- const at = introducedAt(projectRoot, path.relative(projectRoot, file).split(path.sep).join('/'), e, ob);
58
- if (at) return at;
71
+ /**
72
+ * The commit time of the oldest commit, in the unbroken run back from HEAD, whose state file holds the
73
+ * record. The walk reads MAX_COMMITS commits; the next one only says whether the last of them introduced it.
74
+ * In a shallow clone a commit with no parent shown is the boundary: every file seems to appear there, so a
75
+ * record reaching it has no known commit time.
76
+ */
77
+ function introducedAt(h: StateHistory, e: ReadDecision, ob: string, scope: string, shallow: () => boolean): MigratedReview | null {
78
+ let run = 0;
79
+ while (run < h.commits.length && holds(h.commits[run].state, e, ob)) run++;
80
+ const unvouched = (why: string): MigratedReview => ({ at: null, unvouched: `migrated, but its legacy record cannot vouch for it: ${why}` });
81
+ if (run > MAX_COMMITS) {
82
+ return unvouched(`${h.rel} changed in more than ${MAX_COMMITS} commits, and the commit that introduced the record is older than all of them`);
59
83
  }
60
- return null;
84
+ const oldest = run ? h.commits[run - 1] : null;
85
+ if (oldest?.root && shallow()) {
86
+ return unvouched(`the git history is shallow, so the record's real commit time is unknown — fetch the full history `
87
+ + `(\`git fetch --unshallow\`; \`fetch-depth: 0\` in CI), or ${migratedWaiverRemedy(scope, ob)}`);
88
+ }
89
+ return oldest && !Number.isNaN(Date.parse(oldest.at)) ? { at: oldest.at } : null;
61
90
  }
62
91
 
63
- /** When a decision file first appeared: its first commit, else its creation (or last change) on disk. */
64
- export function decisionFirstSeenMs(projectRoot: string, rel: string, abs: string): number {
65
- const added = gitOutput(projectRoot, ['log', '--diff-filter=A', '--format=%cI', '--', rel])?.trim().split('\n').filter(Boolean).pop();
66
- if (added && !Number.isNaN(Date.parse(added))) return Date.parse(added);
67
- try { const s = fs.statSync(abs); return s.birthtimeMs || s.mtimeMs; } catch { return Number.NEGATIVE_INFINITY; }
92
+ /** One run's reads for migrated waivers: each state file's history, decision's first commit and import marker, read once. */
93
+ export function migratedWaiverReview(projectRoot: string) {
94
+ const histories = new Map<string, StateHistory>();
95
+ const bounds = migratedWaiverImportBounds(projectRoot);
96
+ let isShallow: boolean | undefined;
97
+ const shallow = (): boolean => (isShallow ??= isShallowRepository(projectRoot));
98
+ const historyOf = (rel: string): StateHistory => {
99
+ if (!histories.has(rel)) histories.set(rel, readHistory(projectRoot, rel));
100
+ return histories.get(rel)!;
101
+ };
102
+
103
+ return {
104
+ /** How a migrated waiver was reviewed; null when it is not one HEAD's legacy file holds (judged as any other). */
105
+ reviewOf(scope: string, e: ReadDecision): MigratedReview | null {
106
+ const ob = e.payload?.obligation;
107
+ if (e.kind !== 'waiver' || e.source?.where !== 'migrated' || typeof ob !== 'string') return null;
108
+ // The journey keys its state by the unit name it was given: `<name>`, or the catalog id for api units.
109
+ const slugs = new Set([scope.split('/').pop() ?? '', reportSlug(scope.replace(/^(screens|flows)\//, ''))]);
110
+ let unvouched: MigratedReview | null = null;
111
+ for (const slug of slugs) {
112
+ const h = historyOf(`.sungen/journey/${slug}.state.json`);
113
+ if (!holds(h.head, e, ob)) continue;
114
+ const review = introducedAt(h, e, ob, scope, shallow);
115
+ if (review?.at) return { at: review.at, notImported: bounds.notImported(slug, e, review.at) };
116
+ unvouched = unvouched ?? review;
117
+ }
118
+ return unvouched;
119
+ },
120
+ };
68
121
  }