@open-tomato/rafa 0.20.0 → 0.21.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -1773,7 +1773,7 @@ function restoreRunningCommand(previous) {
1773
1773
  var running = null;
1774
1774
 
1775
1775
  // package.json
1776
- var name = "@open-tomato/rafa", version = "0.20.0";
1776
+ var name = "@open-tomato/rafa", version = "0.21.0";
1777
1777
  var init_package = () => {};
1778
1778
 
1779
1779
  // src/cli/version.ts
@@ -2718,6 +2718,22 @@ var init_instinct = __esm(() => {
2718
2718
  ];
2719
2719
  });
2720
2720
 
2721
+ // src/schema/failure-strings.ts
2722
+ function failureStringProblem(entry) {
2723
+ if (entry.trim() === "") {
2724
+ return {
2725
+ code: "empty-failure-string",
2726
+ message: "a blank failure string is found in every text, " + "so every task would read as a recurrence"
2727
+ };
2728
+ }
2729
+ const length = Array.from(entry).length;
2730
+ return length < FAILURE_STRING_MIN_LENGTH ? {
2731
+ code: "short-failure-string",
2732
+ message: `failure string "${entry}" is ${length} characters, under ` + `${FAILURE_STRING_MIN_LENGTH}, so it may match failures the skill does not name`
2733
+ } : null;
2734
+ }
2735
+ var FAILURE_STRING_MIN_LENGTH = 6;
2736
+
2721
2737
  // src/schema/provenance.ts
2722
2738
  function issue2(code, field, message) {
2723
2739
  return { code, field, message };
@@ -3049,6 +3065,9 @@ var init_stack = __esm(() => {
3049
3065
  });
3050
3066
 
3051
3067
  // src/schema/skill.ts
3068
+ function isSkillWarning(code) {
3069
+ return SKILL_WARNING_CODES.includes(code);
3070
+ }
3052
3071
  function countCharacters(text2) {
3053
3072
  return Array.from(text2).length;
3054
3073
  }
@@ -3252,6 +3271,22 @@ function checkPaths(data) {
3252
3271
  }
3253
3272
  return issues;
3254
3273
  }
3274
+ function checkFailureStrings(data) {
3275
+ const value = data["failure_strings"];
3276
+ if (value === undefined)
3277
+ return [];
3278
+ if (!Array.isArray(value)) {
3279
+ return [issue3("wrong-type", "failure_strings", `failure_strings must be a list, not ${typeName3(value)}`)];
3280
+ }
3281
+ return value.flatMap((entry, index) => {
3282
+ const field = `failure_strings[${index}]`;
3283
+ if (typeof entry !== "string") {
3284
+ return [issue3("wrong-type", field, `failure_strings entries must be strings, and this one is ${typeName3(entry)}`)];
3285
+ }
3286
+ const problem = failureStringProblem(entry);
3287
+ return problem === null ? [] : [issue3(problem.code, field, problem.message)];
3288
+ });
3289
+ }
3255
3290
  function checkBoolean(data, field) {
3256
3291
  const value = data[field];
3257
3292
  if (value === undefined || typeof value === "boolean")
@@ -3269,6 +3304,7 @@ function checkSkillFrontmatter(data) {
3269
3304
  ...checkStack(data),
3270
3305
  ...checkWhenToUse(data),
3271
3306
  ...checkTrigger(data),
3307
+ ...checkFailureStrings(data),
3272
3308
  ...SKILL_NAME_LIST_FIELDS.flatMap((field) => checkNameList(data, field)),
3273
3309
  ...checkPaths(data),
3274
3310
  ...SKILL_BOOLEAN_FIELDS.flatMap((field) => checkBoolean(data, field)),
@@ -3276,7 +3312,7 @@ function checkSkillFrontmatter(data) {
3276
3312
  ...checkForbidden(data)
3277
3313
  ];
3278
3314
  }
3279
- var DESCRIPTION_LIMIT = 130, LISTING_LIMIT = 1536, SKILL_SIGNALS, FORBIDDEN_SKILL_FIELDS, SKILL_BOOLEAN_FIELDS, SKILL_NAME_LIST_FIELDS, SKILL_NAME_PATTERN;
3315
+ var DESCRIPTION_LIMIT = 130, LISTING_LIMIT = 1536, SKILL_SIGNALS, FORBIDDEN_SKILL_FIELDS, SKILL_BOOLEAN_FIELDS, SKILL_NAME_LIST_FIELDS, SKILL_NAME_PATTERN, SKILL_WARNING_CODES;
3280
3316
  var init_skill = __esm(() => {
3281
3317
  init_provenance();
3282
3318
  init_stack();
@@ -3285,6 +3321,7 @@ var init_skill = __esm(() => {
3285
3321
  SKILL_BOOLEAN_FIELDS = ["disable-model-invocation", "user-invocable"];
3286
3322
  SKILL_NAME_LIST_FIELDS = ["relates", "supersedes"];
3287
3323
  SKILL_NAME_PATTERN = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
3324
+ SKILL_WARNING_CODES = ["short-failure-string"];
3288
3325
  });
3289
3326
 
3290
3327
  // src/check/layout.ts
@@ -3989,11 +4026,14 @@ function fromLayout(issue4) {
3989
4026
  message: issue4.message
3990
4027
  };
3991
4028
  }
4029
+ function skillSeverity(code) {
4030
+ return isSkillWarning(code) ? "warning" : "failure";
4031
+ }
3992
4032
  function fromSkill(issue4) {
3993
4033
  return {
3994
4034
  stage: "schema",
3995
4035
  code: issue4.code,
3996
- severity: "failure",
4036
+ severity: skillSeverity(issue4.code),
3997
4037
  field: issue4.field,
3998
4038
  line: null,
3999
4039
  message: issue4.message
@@ -5744,131 +5784,9 @@ var init_ndjson = __esm(() => {
5744
5784
  init_types2();
5745
5785
  });
5746
5786
 
5747
- // src/effort/store/sqlite.ts
5748
- import { existsSync as existsSync6, mkdirSync as mkdirSync4 } from "fs";
5749
- import { dirname as dirname8, join as join13 } from "path";
5750
- import { Database } from "bun:sqlite";
5751
- function sqliteStorePath(repoRoot) {
5752
- return join13(repoRoot, EFFORT_STORE_DIR, STORE_FILE_NAME);
5753
- }
5754
- function hasKey(entry) {
5755
- return entry.key !== null;
5756
- }
5757
- function refused2(index, length, reason) {
5758
- return new Error(`effort store: row ${index} of ${length} ${reason}; batch refused`);
5759
- }
5760
- function batchEntries(kind, rows2) {
5761
- const keyOf = EFFORT_KEY_PROJECTIONS[kind];
5762
- const projected = rows2.map((row2) => ({ key: keyOf(row2), row: row2 }));
5763
- const keyed = projected.filter(hasKey);
5764
- if (keyed.length < projected.length) {
5765
- const keylessAt = projected.findIndex((entry) => !hasKey(entry));
5766
- throw refused2(keylessAt, rows2.length, "carries no key");
5767
- }
5768
- const unholdableAt = keyed.findIndex(({ key }) => LONE_SURROGATE.test(key));
5769
- if (unholdableAt !== -1) {
5770
- const reason = "carries a key with a lone UTF-16 surrogate," + " which SQLite text cannot hold";
5771
- throw refused2(unholdableAt, rows2.length, reason);
5772
- }
5773
- return keyed.map(({ key, row: row2 }) => ({ key, body: JSON.stringify(row2) }));
5774
- }
5775
- function userVersion(db) {
5776
- const row2 = db.query("PRAGMA user_version").get();
5777
- return row2?.user_version ?? 0;
5778
- }
5779
- function checkedVersion(db, path, latest) {
5780
- const version2 = userVersion(db);
5781
- if (version2 > latest) {
5782
- throw new Error(`effort store: ${path} is at schema version ${version2}, past the` + ` ${latest} this rafa knows; refusing to read or write it`);
5783
- }
5784
- return version2;
5785
- }
5786
- function migrateSchema(db, path, migrations = SQLITE_MIGRATIONS) {
5787
- const latest = migrations.length;
5788
- if (checkedVersion(db, path, latest) === latest)
5789
- return;
5790
- const applyPending = db.transaction(() => {
5791
- const from = checkedVersion(db, path, latest);
5792
- for (const sql of migrations.slice(from))
5793
- db.run(sql);
5794
- db.run(`PRAGMA user_version = ${latest}`);
5795
- });
5796
- applyPending.immediate();
5797
- }
5798
- function withSqliteStore(path, create, use) {
5799
- if (create)
5800
- mkdirSync4(dirname8(path), { recursive: true });
5801
- const db = new Database(path, { readwrite: true, create });
5802
- try {
5803
- migrateSchema(db, path);
5804
- return use(db);
5805
- } finally {
5806
- db.close();
5807
- }
5808
- }
5809
- function insertBatch(db, table2, entries) {
5810
- const insert = db.query(`INSERT INTO ${table2.name} (${table2.keyColumn}, row_json) VALUES (?, ?)` + ` ON CONFLICT (${table2.keyColumn}) DO NOTHING`);
5811
- const insertAll = db.transaction(() => entries.reduce((appended, { key, body }) => appended + insert.run(key, body).changes, 0));
5812
- return insertAll.immediate();
5813
- }
5814
- function writeSqliteStore(path, rowCount, empty, insert) {
5815
- const exists = existsSync6(path);
5816
- if (rowCount > 0)
5817
- return withSqliteStore(path, !exists, insert);
5818
- if (exists)
5819
- withSqliteStore(path, false, () => {
5820
- return;
5821
- });
5822
- return empty;
5823
- }
5824
- function appendKind2(path, kind, rows2) {
5825
- const entries = batchEntries(kind, rows2);
5826
- const table2 = KIND_TABLES[kind];
5827
- const appended = writeSqliteStore(path, entries.length, 0, (db) => insertBatch(db, table2, entries));
5828
- return { path, appended, skipped: entries.length - appended };
5829
- }
5830
- function keysOfKind2(path, kind) {
5831
- if (!existsSync6(path))
5832
- return new Set;
5833
- const { name: name2, keyColumn } = KIND_TABLES[kind];
5834
- const keys = withSqliteStore(path, false, (db) => db.query(`SELECT ${keyColumn} AS key FROM ${name2} ORDER BY seq`).all());
5835
- return new Set(keys.map(({ key }) => key));
5836
- }
5837
- function parsedObject(body) {
5838
- try {
5839
- const value = JSON.parse(body);
5840
- return typeof value === "object" && value !== null && !Array.isArray(value) ? value : null;
5841
- } catch {
5842
- return null;
5843
- }
5844
- }
5845
- function rowOf(body, where) {
5846
- const value = parsedObject(body);
5847
- if (value === null) {
5848
- throw new Error(`effort store: ${where} does not hold a JSON object;` + " no append writes one");
5849
- }
5850
- return value;
5851
- }
5852
- function readKind2(path, kind) {
5853
- if (!existsSync6(path))
5854
- return [];
5855
- const { name: name2 } = KIND_TABLES[kind];
5856
- const stored = withSqliteStore(path, false, (db) => db.query(`SELECT seq, row_json AS body FROM ${name2} ORDER BY seq`).all());
5857
- return stored.map(({ seq, body }) => rowOf(body, `${path}: ${name2} row ${seq}`));
5858
- }
5859
- function openSqliteStore(repoRoot) {
5860
- const path = sqliteStorePath(repoRoot);
5861
- return {
5862
- append: (kind, rows2) => appendKind2(path, kind, rows2),
5863
- keys: (kind) => keysOfKind2(path, kind),
5864
- read: (kind) => readKind2(path, kind),
5865
- path: () => path
5866
- };
5867
- }
5868
- var STORE_FILE_NAME = "effort.sqlite", SQLITE_MIGRATIONS, SQLITE_SCHEMA_VERSION, KIND_TABLES, LONE_SURROGATE;
5869
- var init_sqlite = __esm(() => {
5870
- init_store();
5871
- init_types2();
5787
+ // src/effort/store/migrations.ts
5788
+ var SQLITE_MIGRATIONS, SQLITE_SCHEMA_VERSION;
5789
+ var init_migrations = __esm(() => {
5872
5790
  SQLITE_MIGRATIONS = [
5873
5791
  `
5874
5792
  CREATE TABLE sessions (
@@ -6044,9 +5962,155 @@ var init_sqlite = __esm(() => {
6044
5962
 
6045
5963
  CREATE UNIQUE INDEX skill_invocations_by_use
6046
5964
  ON skill_invocations (session_id, ifnull(name, ''), ifnull(sidechain, -1));
5965
+ `,
5966
+ `
5967
+ ALTER TABLE task_reports
5968
+ ADD COLUMN skills_used TEXT CHECK (skills_used IS NULL OR json_type(skills_used) = 'array');
5969
+ `,
5970
+ `
5971
+ CREATE TABLE plan_ci (
5972
+ seq INTEGER PRIMARY KEY,
5973
+ plan_stub TEXT NOT NULL CHECK (plan_stub <> ''),
5974
+ pr INTEGER NOT NULL CHECK (typeof(pr) = 'integer' AND pr > 0),
5975
+ head_sha TEXT NOT NULL CHECK (head_sha <> ''),
5976
+ verdict TEXT NOT NULL CHECK (verdict IN ('green', 'red', 'none')),
5977
+ failing TEXT NOT NULL CHECK (json_valid(failing) AND json_type(failing) = 'array'),
5978
+ read_at TEXT NOT NULL CHECK (read_at <> ''),
5979
+ UNIQUE (pr, head_sha, read_at),
5980
+ CHECK ((verdict = 'red') = (json_array_length(failing) > 0))
5981
+ );
6047
5982
  `
6048
5983
  ];
6049
5984
  SQLITE_SCHEMA_VERSION = SQLITE_MIGRATIONS.length;
5985
+ });
5986
+
5987
+ // src/effort/store/sqlite.ts
5988
+ import { existsSync as existsSync6, mkdirSync as mkdirSync4 } from "fs";
5989
+ import { dirname as dirname8, join as join13 } from "path";
5990
+ import { Database } from "bun:sqlite";
5991
+ function sqliteStorePath(repoRoot) {
5992
+ return join13(repoRoot, EFFORT_STORE_DIR, STORE_FILE_NAME);
5993
+ }
5994
+ function hasKey(entry) {
5995
+ return entry.key !== null;
5996
+ }
5997
+ function refused2(index, length, reason) {
5998
+ return new Error(`effort store: row ${index} of ${length} ${reason}; batch refused`);
5999
+ }
6000
+ function batchEntries(kind, rows2) {
6001
+ const keyOf = EFFORT_KEY_PROJECTIONS[kind];
6002
+ const projected = rows2.map((row2) => ({ key: keyOf(row2), row: row2 }));
6003
+ const keyed = projected.filter(hasKey);
6004
+ if (keyed.length < projected.length) {
6005
+ const keylessAt = projected.findIndex((entry) => !hasKey(entry));
6006
+ throw refused2(keylessAt, rows2.length, "carries no key");
6007
+ }
6008
+ const unholdableAt = keyed.findIndex(({ key }) => LONE_SURROGATE.test(key));
6009
+ if (unholdableAt !== -1) {
6010
+ const reason = "carries a key with a lone UTF-16 surrogate," + " which SQLite text cannot hold";
6011
+ throw refused2(unholdableAt, rows2.length, reason);
6012
+ }
6013
+ return keyed.map(({ key, row: row2 }) => ({ key, body: JSON.stringify(row2) }));
6014
+ }
6015
+ function userVersion(db) {
6016
+ const row2 = db.query("PRAGMA user_version").get();
6017
+ return row2?.user_version ?? 0;
6018
+ }
6019
+ function checkedVersion(db, path, latest) {
6020
+ const version2 = userVersion(db);
6021
+ if (version2 > latest) {
6022
+ throw new Error(`effort store: ${path} is at schema version ${version2}, past the` + ` ${latest} this rafa knows; refusing to read or write it`);
6023
+ }
6024
+ return version2;
6025
+ }
6026
+ function migrateSchema(db, path, migrations = SQLITE_MIGRATIONS) {
6027
+ const latest = migrations.length;
6028
+ if (checkedVersion(db, path, latest) === latest)
6029
+ return;
6030
+ const applyPending = db.transaction(() => {
6031
+ const from = checkedVersion(db, path, latest);
6032
+ for (const sql of migrations.slice(from))
6033
+ db.run(sql);
6034
+ db.run(`PRAGMA user_version = ${latest}`);
6035
+ });
6036
+ applyPending.immediate();
6037
+ }
6038
+ function withSqliteStore(path, create, use) {
6039
+ if (create)
6040
+ mkdirSync4(dirname8(path), { recursive: true });
6041
+ const db = new Database(path, { readwrite: true, create });
6042
+ try {
6043
+ migrateSchema(db, path);
6044
+ return use(db);
6045
+ } finally {
6046
+ db.close();
6047
+ }
6048
+ }
6049
+ function insertBatch(db, table2, entries) {
6050
+ const insert = db.query(`INSERT INTO ${table2.name} (${table2.keyColumn}, row_json) VALUES (?, ?)` + ` ON CONFLICT (${table2.keyColumn}) DO NOTHING`);
6051
+ const insertAll = db.transaction(() => entries.reduce((appended, { key, body }) => appended + insert.run(key, body).changes, 0));
6052
+ return insertAll.immediate();
6053
+ }
6054
+ function writeSqliteStore(path, rowCount, empty, insert) {
6055
+ const exists = existsSync6(path);
6056
+ if (rowCount > 0)
6057
+ return withSqliteStore(path, !exists, insert);
6058
+ if (exists)
6059
+ withSqliteStore(path, false, () => {
6060
+ return;
6061
+ });
6062
+ return empty;
6063
+ }
6064
+ function appendKind2(path, kind, rows2) {
6065
+ const entries = batchEntries(kind, rows2);
6066
+ const table2 = KIND_TABLES[kind];
6067
+ const appended = writeSqliteStore(path, entries.length, 0, (db) => insertBatch(db, table2, entries));
6068
+ return { path, appended, skipped: entries.length - appended };
6069
+ }
6070
+ function keysOfKind2(path, kind) {
6071
+ if (!existsSync6(path))
6072
+ return new Set;
6073
+ const { name: name2, keyColumn } = KIND_TABLES[kind];
6074
+ const keys = withSqliteStore(path, false, (db) => db.query(`SELECT ${keyColumn} AS key FROM ${name2} ORDER BY seq`).all());
6075
+ return new Set(keys.map(({ key }) => key));
6076
+ }
6077
+ function parsedObject(body) {
6078
+ try {
6079
+ const value = JSON.parse(body);
6080
+ return typeof value === "object" && value !== null && !Array.isArray(value) ? value : null;
6081
+ } catch {
6082
+ return null;
6083
+ }
6084
+ }
6085
+ function rowOf(body, where) {
6086
+ const value = parsedObject(body);
6087
+ if (value === null) {
6088
+ throw new Error(`effort store: ${where} does not hold a JSON object;` + " no append writes one");
6089
+ }
6090
+ return value;
6091
+ }
6092
+ function readKind2(path, kind) {
6093
+ if (!existsSync6(path))
6094
+ return [];
6095
+ const { name: name2 } = KIND_TABLES[kind];
6096
+ const stored = withSqliteStore(path, false, (db) => db.query(`SELECT seq, row_json AS body FROM ${name2} ORDER BY seq`).all());
6097
+ return stored.map(({ seq, body }) => rowOf(body, `${path}: ${name2} row ${seq}`));
6098
+ }
6099
+ function openSqliteStore(repoRoot) {
6100
+ const path = sqliteStorePath(repoRoot);
6101
+ return {
6102
+ append: (kind, rows2) => appendKind2(path, kind, rows2),
6103
+ keys: (kind) => keysOfKind2(path, kind),
6104
+ read: (kind) => readKind2(path, kind),
6105
+ path: () => path
6106
+ };
6107
+ }
6108
+ var STORE_FILE_NAME = "effort.sqlite", KIND_TABLES, LONE_SURROGATE;
6109
+ var init_sqlite = __esm(() => {
6110
+ init_store();
6111
+ init_migrations();
6112
+ init_types2();
6113
+ init_migrations();
6050
6114
  KIND_TABLES = {
6051
6115
  sessions: { name: "sessions", keyColumn: "session_id" },
6052
6116
  commits: { name: "commits", keyColumn: "sha" }
@@ -12488,9 +12552,21 @@ function checkStatus(status) {
12488
12552
  const expected2 = REPORT_STATUSES.join(", ");
12489
12553
  throw refusedWrite5(`has status ${describeValue7(status)}, not null or one of ${expected2}`);
12490
12554
  }
12555
+ function checkSkillsUsed(skills) {
12556
+ if (skills === undefined || skills === null)
12557
+ return;
12558
+ const isName = (name2) => typeof name2 === "string" && name2.trim().length > 0;
12559
+ if (Array.isArray(skills) && skills.every(isName))
12560
+ return;
12561
+ throw refusedWrite5(`has skills used ${describeValue7(skills)}, not null or a list of non-blank strings`);
12562
+ }
12563
+ function skillsUsedJson(skills) {
12564
+ return skills === undefined || skills === null ? null : JSON.stringify(skills);
12565
+ }
12491
12566
  function writeTaskReport(repoRoot, write, seams = {}) {
12492
12567
  checkDispatch("task report", write.dispatch, write.outcome);
12493
12568
  checkStatus(write.report.status);
12569
+ checkSkillsUsed(write.report.skillsUsed);
12494
12570
  const path = sqliteStorePath(repoRoot);
12495
12571
  const { sessionId, planStub, taskLine: taskLine2 } = write.dispatch;
12496
12572
  const collectedAt = (seams.now ?? (() => new Date))().toISOString();
@@ -12502,7 +12578,8 @@ function writeTaskReport(repoRoot, write, seams = {}) {
12502
12578
  taskLine2,
12503
12579
  write.report.status,
12504
12580
  write.outcome,
12505
- collectedAt
12581
+ collectedAt,
12582
+ skillsUsedJson(write.report.skillsUsed)
12506
12583
  ];
12507
12584
  const appended = writeSqliteStore(path, ROWS_PER_WRITE2, 0, (db) => db.query(INSERT_REPORT).run(...values).changes);
12508
12585
  return { path, appended, skipped: ROWS_PER_WRITE2 - appended };
@@ -12519,19 +12596,45 @@ function readTaskReportTallies(repoRoot) {
12519
12596
  reports: row2.reports
12520
12597
  }));
12521
12598
  }
12599
+ function parseSkillsUsed(sessionId, json) {
12600
+ if (json === null)
12601
+ return null;
12602
+ const list = JSON.parse(json);
12603
+ if (Array.isArray(list) && list.every((name2) => typeof name2 === "string"))
12604
+ return list;
12605
+ throw new Error(`effort store: task report of session ${sessionId} holds skills_used ${json}, not a list of names`);
12606
+ }
12607
+ function readReportedSkills(repoRoot) {
12608
+ const path = sqliteStorePath(repoRoot);
12609
+ if (!existsSync14(path))
12610
+ return [];
12611
+ const rows2 = withSqliteStore(path, false, (db) => db.query(SELECT_REPORTED_SKILLS).all());
12612
+ return rows2.map((row2) => ({
12613
+ sessionId: row2.session_id,
12614
+ planStub: row2.plan_stub,
12615
+ taskLine: row2.task_line,
12616
+ outcome: row2.outcome,
12617
+ skillsUsed: parseSkillsUsed(row2.session_id, row2.skills_used)
12618
+ }));
12619
+ }
12522
12620
  var ROWS_PER_WRITE2 = 1, INSERT_REPORT = `
12523
12621
  INSERT INTO task_reports (
12524
12622
  id, session_id, plan_stub, task_line,
12525
12623
  status,
12526
- outcome, collected_at
12624
+ outcome, collected_at,
12625
+ skills_used
12527
12626
  )
12528
- VALUES (?, ?, ?, ?, ?, ?, ?)
12627
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?)
12529
12628
  ON CONFLICT (session_id) DO NOTHING
12530
12629
  `, SELECT_TALLIES = `
12531
12630
  SELECT plan_stub, status, outcome, COUNT(*) AS reports
12532
12631
  FROM task_reports
12533
12632
  GROUP BY plan_stub, status, outcome
12534
12633
  ORDER BY plan_stub, status, outcome
12634
+ `, SELECT_REPORTED_SKILLS = `
12635
+ SELECT session_id, plan_stub, task_line, outcome, skills_used
12636
+ FROM task_reports
12637
+ ORDER BY seq
12535
12638
  `;
12536
12639
  var init_reports = __esm(() => {
12537
12640
  init_parse();
@@ -25888,6 +25991,80 @@ var init_fix_schema2 = __esm(() => {
25888
25991
  fix_schema_default = createFixSchemaCommand();
25889
25992
  });
25890
25993
 
25994
+ // src/effort/report-args.ts
25995
+ function splitList(raw) {
25996
+ return raw.split(",").map((part) => part.trim()).filter((part) => part.length > 0);
25997
+ }
25998
+ function asSessionKind(value) {
25999
+ return SESSION_KINDS.find((kind) => kind === value) ?? null;
26000
+ }
26001
+ function combinationErrors(skills, planTyped, filters) {
26002
+ if (!skills) {
26003
+ return planTyped ? ["--plan narrows the skills report and needs --skills"] : [];
26004
+ }
26005
+ return filters.map((flag2) => `${flag2} narrows the session tables, which --skills does not print`);
26006
+ }
26007
+ function parseReportArgs(args) {
26008
+ const errors = [];
26009
+ const kinds = [];
26010
+ const entrypoints = [];
26011
+ const plans = [];
26012
+ const filters = [];
26013
+ let json = false;
26014
+ let skills = false;
26015
+ let planTyped = false;
26016
+ for (const arg of args) {
26017
+ if (arg === "--json") {
26018
+ json = true;
26019
+ } else if (arg === "--skills") {
26020
+ skills = true;
26021
+ } else if (arg === "--kind" || arg === "--entrypoint" || arg === "--plan") {
26022
+ errors.push(`${arg} takes a value, as ${arg}=<value>`);
26023
+ } else if (arg.startsWith("--plan=")) {
26024
+ planTyped = true;
26025
+ const stubs = splitList(arg.slice("--plan=".length));
26026
+ if (stubs.length === 0)
26027
+ errors.push(`--plan names no plan stub: ${arg}`);
26028
+ for (const stub of stubs) {
26029
+ if (!plans.includes(stub))
26030
+ plans.push(stub);
26031
+ }
26032
+ } else if (arg.startsWith("--kind=")) {
26033
+ if (!filters.includes("--kind"))
26034
+ filters.push("--kind");
26035
+ for (const value of splitList(arg.slice("--kind=".length))) {
26036
+ const kind = asSessionKind(value);
26037
+ if (kind === null) {
26038
+ errors.push(`--kind value is not a session kind: ${value}` + ` (one of ${SESSION_KINDS.join(", ")})`);
26039
+ } else if (!kinds.includes(kind)) {
26040
+ kinds.push(kind);
26041
+ }
26042
+ }
26043
+ } else if (arg.startsWith("--entrypoint=")) {
26044
+ if (!filters.includes("--entrypoint"))
26045
+ filters.push("--entrypoint");
26046
+ for (const value of splitList(arg.slice("--entrypoint=".length))) {
26047
+ if (!entrypoints.includes(value))
26048
+ entrypoints.push(value);
26049
+ }
26050
+ } else {
26051
+ errors.push(`unrecognised argument: ${arg}`);
26052
+ }
26053
+ }
26054
+ errors.push(...combinationErrors(skills, planTyped, filters));
26055
+ return {
26056
+ json,
26057
+ kinds: kinds.length === 0 ? null : kinds,
26058
+ entrypoints: entrypoints.length === 0 ? null : entrypoints,
26059
+ skills,
26060
+ plans: plans.length === 0 ? null : plans,
26061
+ errors
26062
+ };
26063
+ }
26064
+ var init_report_args = __esm(() => {
26065
+ init_report();
26066
+ });
26067
+
25891
26068
  // src/effort/report-budgets.ts
25892
26069
  function usageOf2(row3) {
25893
26070
  const { usage } = row3;
@@ -26110,6 +26287,607 @@ var init_report_format = __esm(() => {
26110
26287
  BUDGET_RIGHT_ALIGNED = new Set(BUDGET_COLUMNS.slice(2));
26111
26288
  });
26112
26289
 
26290
+ // src/effort/report-skills-format.ts
26291
+ function matchedCell(matches) {
26292
+ const texts = [...new Set(matches.map(({ text: text2 }) => oneLine5(text2)))];
26293
+ return texts.length === 0 ? "-" : texts.join(" / ");
26294
+ }
26295
+ function skillSignalCell(row3) {
26296
+ if (row3.signal === null)
26297
+ return "unknown";
26298
+ if (row3.signal === "unmeasured")
26299
+ return `invoked ${row3.uptake}/${row3.offered}, recurrence not declared`;
26300
+ return row3.signal;
26301
+ }
26302
+ function skillCells(row3) {
26303
+ return [
26304
+ row3.name,
26305
+ formatCount(row3.offered),
26306
+ formatCount(row3.invoked),
26307
+ formatCount(row3.reported),
26308
+ formatCount(row3.recurred),
26309
+ matchedCell(row3.matches),
26310
+ skillSignalCell(row3)
26311
+ ];
26312
+ }
26313
+ function lessonCells(row3) {
26314
+ return [
26315
+ row3.id,
26316
+ row3.artifact === null ? "unknown" : oneLine5(row3.artifact),
26317
+ formatCount(row3.injected),
26318
+ formatCount(row3.recurred),
26319
+ matchedCell(row3.matches),
26320
+ row3.signal
26321
+ ];
26322
+ }
26323
+ function neverOfferedLine(tallies) {
26324
+ if (tallies.length === 0)
26325
+ return "never offered: none";
26326
+ const entries = tallies.map((tally2) => `${tally2.name} (invoked ${formatCount(tally2.invoked)}, recurred ${formatCount(tally2.recurred)})`);
26327
+ return `never offered: ${entries.join(", ")}`;
26328
+ }
26329
+ function formatMetric(value) {
26330
+ const percent = value.percent === null ? "-" : `${value.percent.toFixed(PERCENT_DECIMALS)}%`;
26331
+ return `${formatCount(value.numerator)}/${formatCount(value.denominator)}, ${percent}`;
26332
+ }
26333
+ function formatPlanCi(reading2) {
26334
+ if (reading2 === null)
26335
+ return `${PLAN_CI_LABEL}: not read`;
26336
+ const failing = reading2.failing.length === 0 ? "" : `, failing: ${reading2.failing.map(oneLine5).join(", ")}`;
26337
+ const sha = reading2.headSha.slice(0, SHORT_SHA_LENGTH);
26338
+ return `${PLAN_CI_LABEL}: ${reading2.verdict} on #${reading2.pr} at ${sha}, read ${reading2.readAt}${failing}`;
26339
+ }
26340
+ function resolverName(resolver) {
26341
+ return resolver ?? "not recorded";
26342
+ }
26343
+ function formatSkillsArm(arm) {
26344
+ const skills = arm.skills.length === 0 ? ["skills: none offered"] : alignRows(SKILL_COLUMNS, arm.skills.map(skillCells), COUNT_COLUMNS);
26345
+ const lessons = arm.lessons.length === 0 ? ["lessons: none injected"] : alignRows(LESSON_COLUMNS, arm.lessons.map(lessonCells), COUNT_COLUMNS);
26346
+ const unknown = arm.unknownSessions === 0 ? [] : [`note: ${formatCount(arm.unknownSessions)} of ${formatCount(arm.sessions)} sessions have unknown invocations, left out of M1 and M2`];
26347
+ return [
26348
+ ...skills,
26349
+ neverOfferedLine(arm.neverOffered),
26350
+ "",
26351
+ ...lessons,
26352
+ "",
26353
+ `M1 ${formatMetric(arm.m1)} (task lines invoking a skill)`,
26354
+ `M2 ${formatMetric(arm.m2)} (offered skills invoked)`,
26355
+ ...unknown
26356
+ ];
26357
+ }
26358
+ function indented(indent, line) {
26359
+ return line === "" ? "" : `${indent}${line}`;
26360
+ }
26361
+ function formatSkillsPlan(plan) {
26362
+ const name2 = plan.planStub === null ? "no plan" : `plan ${plan.planStub}`;
26363
+ const sessions = `${formatCount(plan.sessions)} sessions`;
26364
+ const [only] = plan.resolvers;
26365
+ if (plan.resolvers.length === 1 && only !== undefined) {
26366
+ return [
26367
+ `${name2}: ${sessions}, resolver ${resolverName(only.resolver)}`,
26368
+ formatPlanCi(plan.planCi),
26369
+ "",
26370
+ ...formatSkillsArm(only)
26371
+ ];
26372
+ }
26373
+ const arms = plan.resolvers.flatMap((arm) => [
26374
+ "",
26375
+ `${ARM_INDENT}resolver ${resolverName(arm.resolver)}: ${formatCount(arm.sessions)} sessions`,
26376
+ ...formatSkillsArm(arm).map((line) => indented(`${ARM_INDENT}${ARM_INDENT}`, line))
26377
+ ]);
26378
+ return [`${name2}: ${sessions}, ${plan.resolvers.length} resolvers`, formatPlanCi(plan.planCi), ...arms];
26379
+ }
26380
+ function formatSkillsReport(report) {
26381
+ const plans = report.plans.length === 0 ? ["", "no task sessions recorded for the plans read"] : report.plans.flatMap((plan) => ["", ...formatSkillsPlan(plan)]);
26382
+ return [SKILLS_REPORT_HEADER, ...plans, "", ...PROBE_FOOTNOTE];
26383
+ }
26384
+ var SKILLS_REPORT_HEADER = "Co-occurrence, not cause: a skill can be invoked and its failure recur for reasons the skill does not cover.", PROBE_FOOTNOTE, PLAN_CI_LABEL = "plan CI", SKILL_COLUMNS, LESSON_COLUMNS, COUNT_COLUMNS, SHORT_SHA_LENGTH = 7, PERCENT_DECIMALS = 1, ARM_INDENT = " ";
26385
+ var init_report_skills_format = __esm(() => {
26386
+ init_report_format();
26387
+ PROBE_FOOTNOTE = [
26388
+ "note: invoked counts are read from each session's own log. Claude Code's",
26389
+ " own usage record (pluginUsage in ~/.claude/.claude.json) counts per",
26390
+ " plugin and never a project skill, so it has no count to agree with."
26391
+ ];
26392
+ SKILL_COLUMNS = ["skill", "offered", "invoked", "reported", "recurred", "matched", "signal"];
26393
+ LESSON_COLUMNS = ["lesson", "artifact", "injected", "recurred", "matched", "signal"];
26394
+ COUNT_COLUMNS = new Set(["offered", "invoked", "reported", "recurred", "injected"]);
26395
+ });
26396
+
26397
+ // src/effort/recurrence.ts
26398
+ function findingTexts(fact, finding2) {
26399
+ return FINDING_FIELDS.map((field5) => ({
26400
+ source: "finding",
26401
+ sessionId: fact.sessionId,
26402
+ taskLine: fact.taskLine,
26403
+ field: field5,
26404
+ text: finding2[field5]
26405
+ }));
26406
+ }
26407
+ function entryTexts(fact, source, entry) {
26408
+ return ENTRY_FIELDS.map((field5) => ({
26409
+ source,
26410
+ sessionId: fact.sessionId,
26411
+ taskLine: fact.taskLine,
26412
+ field: field5,
26413
+ text: entry[field5]
26414
+ }));
26415
+ }
26416
+ function taskTexts(fact) {
26417
+ return [
26418
+ ...fact.findings.flatMap((finding2) => findingTexts(fact, finding2)),
26419
+ ...fact.blockers.flatMap((entry) => entryTexts(fact, "blocker", entry)),
26420
+ ...fact.outOfScopeBugs.flatMap((entry) => entryTexts(fact, "out-of-scope-bug", entry))
26421
+ ];
26422
+ }
26423
+ function tasksFrom(facts, fromIndex) {
26424
+ const task = facts[fromIndex];
26425
+ if (!Number.isInteger(fromIndex) || task === undefined) {
26426
+ throw new RangeError(`recurrence: fromIndex ${fromIndex} is not a row of ${facts.length} fact rows`);
26427
+ }
26428
+ if (task.planStub === null)
26429
+ return [task];
26430
+ return facts.slice(fromIndex).filter(({ planStub: planStub2 }) => planStub2 === task.planStub);
26431
+ }
26432
+ function failingNames(planStub2, planCi) {
26433
+ if (planStub2 === null)
26434
+ return [];
26435
+ const names = new Set(planCi.filter((row3) => row3.planStub === planStub2).flatMap(({ failing }) => failing));
26436
+ return [...names].map((text2) => ({ source: "plan-ci", sessionId: null, taskLine: null, field: "failing", text: text2 }));
26437
+ }
26438
+ function findRecurrences(strings, facts, fromIndex, planCi) {
26439
+ const tasks = tasksFrom(facts, fromIndex);
26440
+ const searched = [...new Set(strings)].filter((string) => string !== "");
26441
+ const haystacks = [...tasks.flatMap(taskTexts), ...failingNames(tasks[0]?.planStub ?? null, planCi)];
26442
+ return haystacks.flatMap(({ text: text2, ...where }) => {
26443
+ if (text2 === null)
26444
+ return [];
26445
+ return searched.filter((string) => text2.includes(string)).map((string) => ({ string, ...where, text: text2 }));
26446
+ });
26447
+ }
26448
+ var FINDING_FIELDS, ENTRY_FIELDS;
26449
+ var init_recurrence = __esm(() => {
26450
+ FINDING_FIELDS = ["trigger", "what", "cause", "artifact"];
26451
+ ENTRY_FIELDS = ["what", "artifact"];
26452
+ });
26453
+
26454
+ // src/effort/store/plan-ci.ts
26455
+ import { existsSync as existsSync29, readdirSync as readdirSync10 } from "fs";
26456
+ import { resolve as resolve17 } from "path";
26457
+ function refusedWrite7(reason) {
26458
+ return new Error(`effort store: plan CI write ${reason}; nothing written`);
26459
+ }
26460
+ function checkText3(name2, value) {
26461
+ const problem = value === null ? "is null" : textProblem(value);
26462
+ if (problem !== null)
26463
+ throw refusedWrite7(`has a ${name2} that ${problem}`);
26464
+ }
26465
+ function checkRow(row3) {
26466
+ checkText3("plan stub", row3.planStub);
26467
+ checkText3("head sha", row3.headSha);
26468
+ checkText3("read time", row3.readAt);
26469
+ if (!Number.isSafeInteger(row3.pr) || row3.pr <= 0) {
26470
+ throw refusedWrite7(`has pull request ${describeValue7(row3.pr)}, not a whole number above zero`);
26471
+ }
26472
+ const verdict = row3.verdict;
26473
+ if (!PLAN_CI_VERDICTS.some((known) => known === verdict)) {
26474
+ throw refusedWrite7(`has verdict ${describeValue7(verdict)}, not one of ${PLAN_CI_VERDICTS.join(", ")}`);
26475
+ }
26476
+ const failing = row3.failing;
26477
+ if (!Array.isArray(failing))
26478
+ throw refusedWrite7(`has failing ${describeValue7(failing)}, not a list`);
26479
+ for (const name2 of failing)
26480
+ checkText3("failing check name", name2);
26481
+ if (row3.verdict === "red" !== failing.length > 0) {
26482
+ throw refusedWrite7(`has verdict ${row3.verdict} beside ${failing.length} failing checks`);
26483
+ }
26484
+ }
26485
+ function planStubOfBranch(headRefName, planStubs) {
26486
+ const branchStub = attributeBranch(headRefName).stub;
26487
+ if (branchStub === null)
26488
+ return null;
26489
+ const resolution = resolvePlanStub(branchStub, planStubs);
26490
+ if (resolution.stub !== null)
26491
+ return resolution.stub;
26492
+ return resolution.match === "ambiguous" ? null : branchStub;
26493
+ }
26494
+ function planCiReading(pullRequest, rows2, planStubs, readAt) {
26495
+ const verdict = verdictOf2(rows2);
26496
+ if (verdict === "pending") {
26497
+ return { row: null, skipped: `the checks of #${pullRequest.number} are still running` };
26498
+ }
26499
+ const planStub2 = planStubOfBranch(pullRequest.headRefName, planStubs);
26500
+ if (planStub2 === null) {
26501
+ return { row: null, skipped: `the branch "${pullRequest.headRefName}" of #${pullRequest.number} resolves to no plan` };
26502
+ }
26503
+ const failing = failingRows(rows2).map(({ name: name2 }) => name2);
26504
+ return {
26505
+ row: { planStub: planStub2, pr: pullRequest.number, headSha: pullRequest.headRefOid, verdict, failing, readAt },
26506
+ skipped: null
26507
+ };
26508
+ }
26509
+ function writePlanCi(repoRoot, row3) {
26510
+ checkRow(row3);
26511
+ const path4 = sqliteStorePath(repoRoot);
26512
+ const values = [row3.planStub, row3.pr, row3.headSha, row3.verdict, JSON.stringify(row3.failing), row3.readAt];
26513
+ const appended2 = writeSqliteStore(path4, 1, 0, (db) => db.query(INSERT_PLAN_CI).run(...values).changes);
26514
+ return { path: path4, appended: appended2 };
26515
+ }
26516
+ function rowOf4(stored) {
26517
+ const failing = JSON.parse(stored.failing);
26518
+ return {
26519
+ planStub: stored.plan_stub,
26520
+ pr: stored.pr,
26521
+ headSha: stored.head_sha,
26522
+ verdict: stored.verdict,
26523
+ failing: Array.isArray(failing) ? failing.map(String) : [],
26524
+ readAt: stored.read_at
26525
+ };
26526
+ }
26527
+ function readPlanCi(repoRoot, planStub2) {
26528
+ const path4 = sqliteStorePath(repoRoot);
26529
+ if (!existsSync29(path4))
26530
+ return [];
26531
+ const rows2 = withSqliteStore(path4, false, (db) => planStub2 === undefined ? db.query(`${SELECT_COLUMNS} ORDER BY seq`).all() : db.query(`${SELECT_COLUMNS} WHERE plan_stub = ? ORDER BY seq`).all(planStub2));
26532
+ return rows2.map(rowOf4);
26533
+ }
26534
+ function planStubsUnder(repoRoot, planDir) {
26535
+ const dir = resolve17(repoRoot, planDir);
26536
+ return existsSync29(dir) ? planStubsFromFileNames(readdirSync10(dir)) : [];
26537
+ }
26538
+ function messageOf12(error) {
26539
+ return error instanceof Error ? error.message : String(error);
26540
+ }
26541
+ function recordPlanCi(input) {
26542
+ const now = input.now ?? (() => new Date);
26543
+ let row3 = null;
26544
+ try {
26545
+ const planStubs = planStubsUnder(input.repoRoot, input.planDir);
26546
+ const reading2 = planCiReading(input.pullRequest, input.rows, planStubs, now().toISOString());
26547
+ if (reading2.row === null)
26548
+ return { row: null, skipped: reading2.skipped, appended: 0, problem: null };
26549
+ row3 = reading2.row;
26550
+ const { appended: appended2 } = writePlanCi(input.repoRoot, row3);
26551
+ return { row: row3, skipped: null, appended: appended2, problem: null };
26552
+ } catch (error) {
26553
+ const problem = `the CI reading of #${input.pullRequest.number} was not stored: ${messageOf12(error)}`;
26554
+ input.warn(problem);
26555
+ return { row: row3, skipped: null, appended: 0, problem };
26556
+ }
26557
+ }
26558
+ var PLAN_CI_VERDICTS, INSERT_PLAN_CI = `
26559
+ INSERT INTO plan_ci (plan_stub, pr, head_sha, verdict, failing, read_at)
26560
+ VALUES (?, ?, ?, ?, ?, ?)
26561
+ ON CONFLICT (pr, head_sha, read_at) DO NOTHING
26562
+ `, SELECT_COLUMNS = "SELECT plan_stub, pr, head_sha, verdict, failing, read_at FROM plan_ci";
26563
+ var init_plan_ci = __esm(() => {
26564
+ init_checks();
26565
+ init_attribution();
26566
+ init_findings();
26567
+ init_sqlite();
26568
+ PLAN_CI_VERDICTS = ["green", "red", "none"];
26569
+ });
26570
+
26571
+ // src/effort/skill-facts.ts
26572
+ import { existsSync as existsSync30 } from "fs";
26573
+ function selectEntries(table2) {
26574
+ return `SELECT session_id, what, artifact FROM ${table2} ORDER BY seq`;
26575
+ }
26576
+ function parseNames(column, sessionId, json) {
26577
+ if (json === null)
26578
+ return null;
26579
+ const list = JSON.parse(json);
26580
+ if (Array.isArray(list) && list.every((name2) => typeof name2 === "string"))
26581
+ return list;
26582
+ throw new Error(`effort store: ${column} of session ${sessionId} holds ${json}, not a list of names`);
26583
+ }
26584
+ function bySession(rows2, toEntry) {
26585
+ const groups = new Map;
26586
+ for (const row3 of rows2) {
26587
+ const group = groups.get(row3.session_id) ?? [];
26588
+ groups.set(row3.session_id, [...group, toEntry(row3)]);
26589
+ }
26590
+ return groups;
26591
+ }
26592
+ function entryOf({ what, artifact }) {
26593
+ return { what, artifact };
26594
+ }
26595
+ function findingOf(row3) {
26596
+ return {
26597
+ kind: row3.kind,
26598
+ trigger: row3.trigger,
26599
+ what: row3.what,
26600
+ cause: row3.cause,
26601
+ resolution: row3.resolution,
26602
+ artifact: row3.artifact,
26603
+ signal: row3.signal
26604
+ };
26605
+ }
26606
+ function readSessionRows(path4) {
26607
+ return withSqliteStore(path4, false, (db) => {
26608
+ const offers = db.query(SELECT_OFFERS).all();
26609
+ return {
26610
+ offers: new Map(offers.map((row3) => [row3.session_id, {
26611
+ resolver: row3.resolver,
26612
+ skillsOffered: parseNames("skills_offered", row3.session_id, row3.skills_offered),
26613
+ lessonsOffered: parseNames("lessons_offered", row3.session_id, row3.lessons_offered)
26614
+ }])),
26615
+ findings: bySession(db.query(SELECT_FINDINGS2).all(), findingOf),
26616
+ blockers: bySession(db.query(selectEntries("blockers")).all(), entryOf),
26617
+ outOfScopeBugs: bySession(db.query(selectEntries("out_of_scope_bugs")).all(), entryOf)
26618
+ };
26619
+ });
26620
+ }
26621
+ function invocationsBySession(invocations) {
26622
+ const sessions = new Map;
26623
+ for (const invocation of invocations) {
26624
+ const held = sessions.get(invocation.sessionId) ?? [];
26625
+ if (held === UNKNOWN_SKILL_COUNT || invocation.count === UNKNOWN_SKILL_COUNT) {
26626
+ sessions.set(invocation.sessionId, UNKNOWN_SKILL_COUNT);
26627
+ continue;
26628
+ }
26629
+ const { name: name2, count: count2 } = invocation;
26630
+ const counted5 = held.some((use) => use.name === name2) ? held.map((use) => use.name === name2 ? { name: name2, count: use.count + count2 } : use) : [...held, { name: name2, count: count2 }];
26631
+ sessions.set(invocation.sessionId, counted5);
26632
+ }
26633
+ return sessions;
26634
+ }
26635
+ function bareNames(names) {
26636
+ return names === null ? null : [...new Set(names.map((name2) => bareSkillName(name2)))];
26637
+ }
26638
+ function orderedReports(reports, plans) {
26639
+ const order = plans === undefined ? [...new Set(reports.map(({ planStub: planStub2 }) => planStub2))] : [...new Set(plans)];
26640
+ return order.flatMap((plan) => reports.filter(({ planStub: planStub2 }) => planStub2 === plan));
26641
+ }
26642
+ function readSkillFacts(repoRoot, plans) {
26643
+ if (!existsSync30(sqliteStorePath(repoRoot)))
26644
+ return [];
26645
+ const reports = orderedReports(readReportedSkills(repoRoot), plans);
26646
+ if (reports.length === 0)
26647
+ return [];
26648
+ const rows2 = readSessionRows(sqliteStorePath(repoRoot));
26649
+ const invoked = invocationsBySession(readSkillInvocations(repoRoot));
26650
+ const planCi = new Map;
26651
+ for (const row3 of readPlanCi(repoRoot))
26652
+ planCi.set(row3.planStub, [...planCi.get(row3.planStub) ?? [], row3]);
26653
+ return reports.map((report) => {
26654
+ const offer = rows2.offers.get(report.sessionId);
26655
+ return {
26656
+ sessionId: report.sessionId,
26657
+ planStub: report.planStub,
26658
+ taskLine: report.taskLine,
26659
+ outcome: report.outcome,
26660
+ skillsUsed: bareNames(report.skillsUsed),
26661
+ resolver: offer?.resolver ?? null,
26662
+ skillsOffered: offer?.skillsOffered ?? null,
26663
+ lessonsOffered: offer?.lessonsOffered ?? null,
26664
+ invoked: invoked.get(report.sessionId) ?? [],
26665
+ findings: rows2.findings.get(report.sessionId) ?? [],
26666
+ blockers: rows2.blockers.get(report.sessionId) ?? [],
26667
+ outOfScopeBugs: rows2.outOfScopeBugs.get(report.sessionId) ?? [],
26668
+ planCi: report.planStub === null ? [] : planCi.get(report.planStub) ?? []
26669
+ };
26670
+ });
26671
+ }
26672
+ var SELECT_OFFERS = "SELECT session_id, resolver, skills_offered, lessons_offered FROM dispatches ORDER BY seq", SELECT_FINDINGS2 = `
26673
+ SELECT session_id, kind, trigger, what, cause, resolution, artifact, signal
26674
+ FROM findings
26675
+ ORDER BY seq
26676
+ `;
26677
+ var init_skill_facts = __esm(() => {
26678
+ init_skill_names();
26679
+ init_plan_ci();
26680
+ init_reports();
26681
+ init_skill_invocations();
26682
+ init_sqlite();
26683
+ });
26684
+
26685
+ // src/effort/skill-signals.ts
26686
+ function invokedNames(fact) {
26687
+ return fact.invoked === UNKNOWN_SKILL_COUNT ? [] : fact.invoked.map(({ name: name2 }) => name2);
26688
+ }
26689
+ function unique(names) {
26690
+ return [...new Set(names)];
26691
+ }
26692
+ function rowsWhere(facts, holds) {
26693
+ return facts.filter(holds).length;
26694
+ }
26695
+ function indicesWhere(facts, holds) {
26696
+ return facts.flatMap((fact, index) => holds(fact) ? [index] : []);
26697
+ }
26698
+ function recurrencesFrom(strings, facts, from, match) {
26699
+ if (strings.length === 0)
26700
+ return { recurred: 0, matches: [] };
26701
+ const found = from.map((index) => match(strings, facts, index, facts[index]?.planCi ?? []));
26702
+ const matches = new Map;
26703
+ for (const recurrence of found.flat()) {
26704
+ const { string, source, sessionId, field: field5, text: text2 } = recurrence;
26705
+ const key = JSON.stringify([string, source, sessionId, field5, text2]);
26706
+ if (!matches.has(key))
26707
+ matches.set(key, recurrence);
26708
+ }
26709
+ return {
26710
+ recurred: found.filter((answer3) => answer3.length > 0).length,
26711
+ matches: [...matches.values()]
26712
+ };
26713
+ }
26714
+ function tallySkill(name2, facts, strings, match) {
26715
+ const offers = (fact) => fact.skillsOffered?.includes(name2) ?? false;
26716
+ const invokes = (fact) => invokedNames(fact).includes(name2);
26717
+ return {
26718
+ name: name2,
26719
+ offered: rowsWhere(facts, offers),
26720
+ invoked: rowsWhere(facts, invokes),
26721
+ uptake: rowsWhere(facts, (fact) => offers(fact) && invokes(fact)),
26722
+ reported: rowsWhere(facts, (fact) => fact.skillsUsed?.includes(name2) ?? false),
26723
+ unknown: rowsWhere(facts, (fact) => offers(fact) && fact.invoked === UNKNOWN_SKILL_COUNT),
26724
+ ...recurrencesFrom(strings, facts, indicesWhere(facts, invokes), match)
26725
+ };
26726
+ }
26727
+ function skillSignalOf(tally2, declared) {
26728
+ if (tally2.recurred > 0)
26729
+ return "recurring";
26730
+ if (tally2.invoked > 0 && !declared)
26731
+ return "unmeasured";
26732
+ if (tally2.invoked > 0)
26733
+ return "earning";
26734
+ if (tally2.unknown > 0)
26735
+ return null;
26736
+ return "ignored";
26737
+ }
26738
+ function skillSignals(facts, match, failureStrings) {
26739
+ const names = unique(facts.flatMap((fact) => [...fact.skillsOffered ?? [], ...invokedNames(fact)]));
26740
+ const tallies = names.map((name2) => tallySkill(name2, facts, failureStrings.get(name2) ?? [], match));
26741
+ return {
26742
+ skills: tallies.filter(({ offered: offered2 }) => offered2 > 0).map((tally2) => ({
26743
+ ...tally2,
26744
+ signal: skillSignalOf(tally2, (failureStrings.get(tally2.name) ?? []).length > 0)
26745
+ })),
26746
+ neverOffered: tallies.filter(({ offered: offered2 }) => offered2 === 0)
26747
+ };
26748
+ }
26749
+ function lessonSignals(facts, match, artifacts) {
26750
+ const ids = unique(facts.flatMap((fact) => fact.lessonsOffered ?? []));
26751
+ return ids.map((id) => {
26752
+ const artifact = artifacts.get(id) ?? null;
26753
+ const injects = (fact) => fact.lessonsOffered?.includes(id) ?? false;
26754
+ const found = recurrencesFrom(artifact === null ? [] : [artifact], facts, indicesWhere(facts, injects), match);
26755
+ return {
26756
+ id,
26757
+ artifact,
26758
+ injected: rowsWhere(facts, injects),
26759
+ ...found,
26760
+ signal: found.recurred > 0 ? "injected-recurring" : "injected"
26761
+ };
26762
+ });
26763
+ }
26764
+ var init_skill_signals = __esm(() => {
26765
+ init_skill_invocations();
26766
+ });
26767
+
26768
+ // src/effort/report-skills.ts
26769
+ import { readFileSync as readFileSync33 } from "fs";
26770
+ function readTextFile2(path4) {
26771
+ try {
26772
+ return readFileSync33(path4, "utf8");
26773
+ } catch {
26774
+ return null;
26775
+ }
26776
+ }
26777
+ function declaredStrings(text2) {
26778
+ const data = text2 === null ? null : readFrontmatter(text2);
26779
+ const value = data?.[FAILURE_STRINGS_KEY];
26780
+ if (!Array.isArray(value))
26781
+ return [];
26782
+ return value.filter((entry) => typeof entry === "string" && entry !== "");
26783
+ }
26784
+ function failureStringsOf(resolution, readText7 = readTextFile2) {
26785
+ const declared = new Map;
26786
+ for (const item of resolution.items) {
26787
+ if (item.kind !== "skill" || item.state !== "served")
26788
+ continue;
26789
+ const strings = declaredStrings(readText7(item.winner.path));
26790
+ if (strings.length > 0)
26791
+ declared.set(item.name, strings);
26792
+ }
26793
+ return declared;
26794
+ }
26795
+ function lessonArtifactsOf(lessons) {
26796
+ const artifacts = new Map;
26797
+ const seen = new Set;
26798
+ for (const { id, artifact } of lessons) {
26799
+ if (seen.has(id))
26800
+ continue;
26801
+ seen.add(id);
26802
+ if (artifact !== null)
26803
+ artifacts.set(id, artifact);
26804
+ }
26805
+ return artifacts;
26806
+ }
26807
+ function readHeldLessons(seams) {
26808
+ return readScopes(seams).flatMap((listing2) => listing2.records.flatMap(({ instinct }) => instinct === null ? [] : [{ id: instinct.id, artifact: instinct.artifact }]));
26809
+ }
26810
+ function groupBy(values, key) {
26811
+ const groups = new Map;
26812
+ for (const value of values) {
26813
+ const group = groups.get(key(value)) ?? [];
26814
+ groups.set(key(value), [...group, value]);
26815
+ }
26816
+ return groups;
26817
+ }
26818
+ function metric(numerator, denominator) {
26819
+ return {
26820
+ numerator,
26821
+ denominator,
26822
+ percent: denominator === 0 ? null : numerator / denominator * 100
26823
+ };
26824
+ }
26825
+ function invokedNames2(fact) {
26826
+ return fact.invoked === UNKNOWN_SKILL_COUNT ? [] : [...new Set(fact.invoked.map(({ name: name2 }) => name2))];
26827
+ }
26828
+ function m1Of(known) {
26829
+ const pairs = new Set(known.flatMap((fact) => invokedNames2(fact).map((name2) => JSON.stringify([fact.taskLine, name2]))));
26830
+ return metric(pairs.size, new Set(known.map(({ taskLine: taskLine2 }) => taskLine2)).size);
26831
+ }
26832
+ function m2Of(known) {
26833
+ const offered2 = known.flatMap((fact) => [...new Set(fact.skillsOffered ?? [])].map((name2) => ({ fact, name: name2 })));
26834
+ const taken = offered2.filter(({ fact, name: name2 }) => invokedNames2(fact).includes(name2));
26835
+ return metric(taken.length, offered2.length);
26836
+ }
26837
+ function planMatcher(planFacts) {
26838
+ const indices = new Map(planFacts.map((fact, index) => [fact, index]));
26839
+ return (strings, facts, fromIndex, planCi) => {
26840
+ const row3 = facts[fromIndex];
26841
+ const index = row3 === undefined ? undefined : indices.get(row3);
26842
+ if (index === undefined) {
26843
+ throw new RangeError(`skills report: fromIndex ${fromIndex} is not a row of the plan's ${planFacts.length}`);
26844
+ }
26845
+ return findRecurrences(strings, planFacts, index, planCi);
26846
+ };
26847
+ }
26848
+ function armOf(resolver, facts, match, failureStrings, artifacts) {
26849
+ const known = facts.filter((fact) => fact.invoked !== UNKNOWN_SKILL_COUNT);
26850
+ const { skills, neverOffered } = skillSignals(facts, match, failureStrings);
26851
+ return {
26852
+ resolver,
26853
+ sessions: facts.length,
26854
+ unknownSessions: facts.length - known.length,
26855
+ skills,
26856
+ neverOffered,
26857
+ lessons: lessonSignals(facts, match, artifacts),
26858
+ m1: m1Of(known),
26859
+ m2: m2Of(known)
26860
+ };
26861
+ }
26862
+ function latestReading(readings) {
26863
+ return readings.reduce((latest, reading2) => latest === null || reading2.readAt >= latest.readAt ? reading2 : latest, null);
26864
+ }
26865
+ function skillsReportOf(facts, failureStrings, artifacts) {
26866
+ const plans = [...groupBy(facts, ({ planStub: planStub2 }) => planStub2)].map(([planStub2, planFacts]) => {
26867
+ const match = planMatcher(planFacts);
26868
+ const arms = [...groupBy(planFacts, ({ resolver }) => resolver)];
26869
+ return {
26870
+ planStub: planStub2,
26871
+ sessions: planFacts.length,
26872
+ planCi: latestReading(planFacts[0]?.planCi ?? []),
26873
+ resolvers: arms.map(([resolver, armFacts]) => armOf(resolver, armFacts, match, failureStrings, artifacts))
26874
+ };
26875
+ });
26876
+ return { plans };
26877
+ }
26878
+ function buildSkillsReport(input) {
26879
+ const facts = readSkillFacts(input.repoRoot, input.plans);
26880
+ return skillsReportOf(facts, failureStringsOf(input.resolution, input.readText ?? readTextFile2), lessonArtifactsOf(input.lessons));
26881
+ }
26882
+ var FAILURE_STRINGS_KEY = "failure_strings";
26883
+ var init_report_skills = __esm(() => {
26884
+ init_instinct_records();
26885
+ init_recurrence();
26886
+ init_skill_facts();
26887
+ init_skill_signals();
26888
+ init_skill_invocations();
26889
+ });
26890
+
26113
26891
  // src/effort/report.ts
26114
26892
  import { homedir as homedir5 } from "os";
26115
26893
  function asReportRows(rows2) {
@@ -26270,47 +27048,6 @@ function summariseSessions(rows2, filters = { kinds: null, entrypoints: null },
26270
27048
  budgets: budgetedSessions(rows2, budgets)
26271
27049
  };
26272
27050
  }
26273
- function splitList(raw) {
26274
- return raw.split(",").map((part) => part.trim()).filter((part) => part.length > 0);
26275
- }
26276
- function asSessionKind(value) {
26277
- return SESSION_KINDS.find((kind) => kind === value) ?? null;
26278
- }
26279
- function parseReportArgs(args) {
26280
- const errors = [];
26281
- const kinds = [];
26282
- const entrypoints = [];
26283
- let json = false;
26284
- for (const arg of args) {
26285
- if (arg === "--json") {
26286
- json = true;
26287
- } else if (arg === "--kind" || arg === "--entrypoint") {
26288
- errors.push(`${arg} takes a value, as ${arg}=<value>`);
26289
- } else if (arg.startsWith("--kind=")) {
26290
- for (const value of splitList(arg.slice("--kind=".length))) {
26291
- const kind = asSessionKind(value);
26292
- if (kind === null) {
26293
- errors.push(`--kind value is not a session kind: ${value}` + ` (one of ${SESSION_KINDS.join(", ")})`);
26294
- } else if (!kinds.includes(kind)) {
26295
- kinds.push(kind);
26296
- }
26297
- }
26298
- } else if (arg.startsWith("--entrypoint=")) {
26299
- for (const value of splitList(arg.slice("--entrypoint=".length))) {
26300
- if (!entrypoints.includes(value))
26301
- entrypoints.push(value);
26302
- }
26303
- } else {
26304
- errors.push(`unrecognised argument: ${arg}`);
26305
- }
26306
- }
26307
- return {
26308
- json,
26309
- kinds: kinds.length === 0 ? null : kinds,
26310
- entrypoints: entrypoints.length === 0 ? null : entrypoints,
26311
- errors
26312
- };
26313
- }
26314
27051
  function resolveStore(store, roots) {
26315
27052
  if (store !== undefined)
26316
27053
  return store;
@@ -26325,14 +27062,50 @@ function buildReport(options) {
26325
27062
  entrypoints: options.entrypoints ?? null
26326
27063
  }, readTaskReportTallies(repoRoot), readPreflightHalts(repoRoot), readSessionBudgets(repoRoot));
26327
27064
  }
27065
+ function readSkillsReport(options) {
27066
+ const { repoRoot: root, home, entry } = options;
27067
+ const { config } = loadConfig({ root, home });
27068
+ const pinned = entry === undefined ? {} : { entry };
27069
+ return buildSkillsReport({
27070
+ repoRoot: root,
27071
+ ...options.plans === null || options.plans === undefined ? {} : { plans: options.plans },
27072
+ resolution: resolveSessionTiers({ root, home, settings: config, ...pinned }),
27073
+ lessons: readHeldLessons({ home, projectRoot: root, ...pinned })
27074
+ });
27075
+ }
26328
27076
  function refuse4(problems) {
26329
27077
  throw new CommandExit(1, problems.map((problem) => `rafa effort report: ${problem}`).join(`
26330
27078
  `));
26331
27079
  }
27080
+ function writeSkillsReport(plans, json, repoRoot) {
27081
+ let built;
27082
+ try {
27083
+ built = readSkillsReport({ repoRoot, home: homedir5(), plans });
27084
+ } catch (error) {
27085
+ if (!(error instanceof ConfigError))
27086
+ throw error;
27087
+ refuse4(error.problems);
27088
+ }
27089
+ const output = activeOutput();
27090
+ if (activeOutputMode() === "json") {
27091
+ output.result(built);
27092
+ return;
27093
+ }
27094
+ if (json) {
27095
+ output.info(JSON.stringify(built, null, 2));
27096
+ return;
27097
+ }
27098
+ for (const line of formatSkillsReport(built))
27099
+ output.info(line);
27100
+ }
26332
27101
  async function report(args, repoRoot) {
26333
27102
  const parsed = parseReportArgs(args);
26334
27103
  if (parsed.errors.length > 0)
26335
27104
  refuse4(parsed.errors);
27105
+ if (parsed.skills) {
27106
+ writeSkillsReport(parsed.plans, parsed.json, repoRoot);
27107
+ return;
27108
+ }
26336
27109
  let built;
26337
27110
  try {
26338
27111
  built = buildReport({
@@ -26372,9 +27145,13 @@ var init_report = __esm(() => {
26372
27145
  init_command();
26373
27146
  init_config_load();
26374
27147
  init_config();
27148
+ init_serving();
26375
27149
  init_classify();
26376
27150
  init_commits();
27151
+ init_report_args();
26377
27152
  init_report_format();
27153
+ init_report_skills_format();
27154
+ init_report_skills();
26378
27155
  init_dispatches();
26379
27156
  init_store2();
26380
27157
  init_preflight();
@@ -26394,7 +27171,7 @@ var init_report2 = __esm(() => {
26394
27171
  subject: "effort",
26395
27172
  action: "report",
26396
27173
  summary: "roll the stored session rows up per plan, then tally the task reports and list preflight halts",
26397
- description: "Rolls the session rows `rafa effort collect` stored up per plan, or per branch for a" + " session no plan claims, then tallies the stored task reports by plan, status and outcome, and" + " lists the runs whose preflight halted, one row per required item that failed. It" + " reads only what is stored, so two runs over an unchanged store print the same bytes. With" + " `--output=json` the report is the data of the terminal result event. An unrecognised argument" + " and a config the loop cannot run on are each refused, one line per problem.",
27174
+ description: "Rolls the session rows `rafa effort collect` stored up per plan, or per branch for a" + " session no plan claims, then tallies the stored task reports by plan, status and outcome, and" + " lists the runs whose preflight halted, one row per required item that failed. It" + " reads only what is stored, so two runs over an unchanged store print the same bytes. With" + " `--output=json` the report is the data of the terminal result event. With `--skills` it prints" + " the skills report instead: per plan, and per resolver within one, whether each offered skill and" + " injected lesson co-occurred with a recurrence of the failure it should prevent, read from the stored" + " rows and the skills' frontmatter, never from a session log. An unrecognised argument" + " and a config the loop cannot run on are each refused, one line per problem.",
26398
27175
  args: [],
26399
27176
  flags: [
26400
27177
  {
@@ -26407,6 +27184,16 @@ var init_report2 = __esm(() => {
26407
27184
  description: "Keeps only the sessions whose dominant entrypoint is one of these, comma-separated." + " `sdk-cli` is the loop.",
26408
27185
  type: "string"
26409
27186
  },
27187
+ {
27188
+ name: "skills",
27189
+ description: "Prints the skills report in place of the session tables: per plan and per resolver," + " each skill's and lesson's signal, the skills invoked and never offered, M1, M2 and plan CI." + " Refused beside `--kind` or `--entrypoint`, which narrow session rows it does not read.",
27190
+ type: "boolean"
27191
+ },
27192
+ {
27193
+ name: "plan",
27194
+ description: "Keeps only these plan stubs in the skills report, comma-separated, and may be" + " repeated. A stub the store holds no row for reads as no plan. Refused without `--skills`.",
27195
+ type: "string"
27196
+ },
26410
27197
  {
26411
27198
  name: "json",
26412
27199
  description: "Deprecated: the phase 0 spelling of `--output=json`, read as that flag after one" + " deprecation line on stderr.",
@@ -26419,6 +27206,10 @@ var init_report2 = __esm(() => {
26419
27206
  cmd: "rafa effort report",
26420
27207
  note: "Prints the per-plan tables, the task report tallies, then the preflight halts."
26421
27208
  },
27209
+ {
27210
+ cmd: "rafa effort report --skills --plan=rafa-24-know-which-skills-earn",
27211
+ note: "Prints one plan's skills and lessons with their signals, per resolver, under the fixed" + " co-occurrence line."
27212
+ },
26422
27213
  {
26423
27214
  cmd: "rafa effort report --entrypoint=sdk-cli --kind=task --output=json",
26424
27215
  note: "Writes a start event, then a result event whose data is the report of the task sessions" + " the loop ran."
@@ -26429,12 +27220,12 @@ var init_report2 = __esm(() => {
26429
27220
  });
26430
27221
 
26431
27222
  // src/agents/vendorable.ts
26432
- import { readdirSync as readdirSync10, readFileSync as readFileSync33 } from "fs";
27223
+ import { readdirSync as readdirSync11, readFileSync as readFileSync34 } from "fs";
26433
27224
  import { isAbsolute as isAbsolute10, join as join41 } from "path";
26434
27225
  function planFiles(dir) {
26435
27226
  let entries;
26436
27227
  try {
26437
- entries = readdirSync10(dir, { withFileTypes: true });
27228
+ entries = readdirSync11(dir, { withFileTypes: true });
26438
27229
  } catch {
26439
27230
  return [];
26440
27231
  }
@@ -26442,7 +27233,7 @@ function planFiles(dir) {
26442
27233
  }
26443
27234
  function readText7(path4) {
26444
27235
  try {
26445
- return readFileSync33(path4, "utf8");
27236
+ return readFileSync34(path4, "utf8");
26446
27237
  } catch {
26447
27238
  return null;
26448
27239
  }
@@ -26491,7 +27282,7 @@ var init_vendorable = __esm(() => {
26491
27282
 
26492
27283
  // src/project/gitignore.ts
26493
27284
  import { createHash as createHash5 } from "crypto";
26494
- import { existsSync as existsSync29, mkdirSync as mkdirSync11, readFileSync as readFileSync34, writeFileSync as writeFileSync10 } from "fs";
27285
+ import { existsSync as existsSync31, mkdirSync as mkdirSync11, readFileSync as readFileSync35, writeFileSync as writeFileSync10 } from "fs";
26495
27286
  import { dirname as dirname15, isAbsolute as isAbsolute11, join as join42 } from "path";
26496
27287
  function trackingEntry(flags) {
26497
27288
  if (flags.trackingAll) {
@@ -26561,9 +27352,9 @@ function attempt2(file, failure6, act) {
26561
27352
  }
26562
27353
  }
26563
27354
  function readIfPresent4(file) {
26564
- if (!existsSync29(file))
27355
+ if (!existsSync31(file))
26565
27356
  return null;
26566
- return attempt2(file, "cannot be read", () => readFileSync34(file, "utf8"));
27357
+ return attempt2(file, "cannot be read", () => readFileSync35(file, "utf8"));
26567
27358
  }
26568
27359
  function writeTrackingGitignore(root, flags) {
26569
27360
  requireAbsolute2(root);
@@ -26640,8 +27431,8 @@ var init_gitignore = __esm(() => {
26640
27431
 
26641
27432
  // src/project/roots.ts
26642
27433
  import { spawnSync as spawnSync4 } from "child_process";
26643
- import { accessSync, constants, readFileSync as readFileSync35, statSync as statSync19 } from "fs";
26644
- import { isAbsolute as isAbsolute12, join as join43, resolve as resolve17 } from "path";
27434
+ import { accessSync, constants, readFileSync as readFileSync36, statSync as statSync19 } from "fs";
27435
+ import { isAbsolute as isAbsolute12, join as join43, resolve as resolve18 } from "path";
26645
27436
  function gitToplevel(dir) {
26646
27437
  const result = spawnSync4("git", ["rev-parse", "--show-toplevel"], {
26647
27438
  cwd: dir,
@@ -26674,7 +27465,7 @@ function realOrSpelled(path4, fs3) {
26674
27465
  try {
26675
27466
  return fs3.realpath(path4);
26676
27467
  } catch {
26677
- return resolve17(path4);
27468
+ return resolve18(path4);
26678
27469
  }
26679
27470
  }
26680
27471
  function namedRoots(home, fs3) {
@@ -26716,7 +27507,7 @@ function writeRefusal(given, real, fs3) {
26716
27507
  }
26717
27508
  function refusalOf2(given, named, fs3) {
26718
27509
  const resolved = resolveReal(given, fs3);
26719
- const byName2 = namedRefusal(given, resolved.real ?? resolve17(given), named);
27510
+ const byName2 = namedRefusal(given, resolved.real ?? resolve18(given), named);
26720
27511
  if (byName2 !== null)
26721
27512
  return byName2;
26722
27513
  if (resolved.real === null) {
@@ -26797,7 +27588,7 @@ var init_roots = __esm(() => {
26797
27588
  ]);
26798
27589
  DISK_ROOTS_FILE_SYSTEM = Object.freeze({
26799
27590
  ...DISK_FILE_SYSTEM,
26800
- readFile: (path4) => readFileSync35(path4, "utf8"),
27591
+ readFile: (path4) => readFileSync36(path4, "utf8"),
26801
27592
  isDirectory: (path4) => statSync19(path4).isDirectory(),
26802
27593
  assertWritable: (path4) => accessSync(path4, constants.W_OK)
26803
27594
  });
@@ -26810,7 +27601,7 @@ var init_roots = __esm(() => {
26810
27601
  });
26811
27602
 
26812
27603
  // src/project/root-choice.ts
26813
- import { resolve as resolve18 } from "path";
27604
+ import { resolve as resolve19 } from "path";
26814
27605
  function chosen(path4, source) {
26815
27606
  return { root: { path: path4, source }, problem: null };
26816
27607
  }
@@ -26843,7 +27634,7 @@ function firstCandidate(found) {
26843
27634
  return first.refusal === null ? chosen(first.path, first.source) : problem(first.refusal.reason);
26844
27635
  }
26845
27636
  function namedRoot(typed, start, source, seams) {
26846
- const path4 = resolve18(start, typed);
27637
+ const path4 = resolve19(start, typed);
26847
27638
  const refusal5 = rootRefusal(path4, seams);
26848
27639
  if (refusal5 !== null)
26849
27640
  return problem(refusal5.reason);
@@ -26893,7 +27684,7 @@ var init_root_choice = __esm(() => {
26893
27684
  });
26894
27685
 
26895
27686
  // src/project/scaffold.ts
26896
- import { existsSync as existsSync30, lstatSync as lstatSync2, mkdirSync as mkdirSync12, statSync as statSync20, writeFileSync as writeFileSync11 } from "fs";
27687
+ import { existsSync as existsSync32, lstatSync as lstatSync2, mkdirSync as mkdirSync12, statSync as statSync20, writeFileSync as writeFileSync11 } from "fs";
26897
27688
  import { join as join44 } from "path";
26898
27689
  function configText(header) {
26899
27690
  return [...header, VERSION_LINE, ...CONFIG_SETTINGS_LINES, ""].join(`
@@ -26918,7 +27709,7 @@ function isDirectory2(path4) {
26918
27709
  function directoryConflict(dir) {
26919
27710
  if (!occupied(dir) || isDirectory2(dir))
26920
27711
  return null;
26921
- return existsSync30(dir) ? `${dir} is not a directory` : `${dir} is a link to nothing`;
27712
+ return existsSync32(dir) ? `${dir} is not a directory` : `${dir} is a link to nothing`;
26922
27713
  }
26923
27714
  function layoutConflicts(layout) {
26924
27715
  const [scopeDir, ...tree] = layout.dirs;
@@ -26926,7 +27717,7 @@ function layoutConflicts(layout) {
26926
27717
  if (opening !== null)
26927
27718
  return [opening];
26928
27719
  const dirs = tree.flatMap((dir) => directoryConflict(dir) ?? []);
26929
- const file = occupied(layout.configFile) && !existsSync30(layout.configFile) ? [`${layout.configFile} is a link to nothing`] : [];
27720
+ const file = occupied(layout.configFile) && !existsSync32(layout.configFile) ? [`${layout.configFile} is a link to nothing`] : [];
26930
27721
  return [...dirs, ...file];
26931
27722
  }
26932
27723
  function scaffoldConflicts(root, home) {
@@ -26940,7 +27731,7 @@ function attempt3(path4, failure6, act) {
26940
27731
  }
26941
27732
  }
26942
27733
  function ensureDirectory(path4) {
26943
- if (existsSync30(path4))
27734
+ if (existsSync32(path4))
26944
27735
  return { path: path4, kind: "directory", change: "unchanged" };
26945
27736
  attempt3(path4, "cannot be created", () => {
26946
27737
  mkdirSync12(path4, { recursive: true });
@@ -26948,7 +27739,7 @@ function ensureDirectory(path4) {
26948
27739
  return { path: path4, kind: "directory", change: "created" };
26949
27740
  }
26950
27741
  function ensureFile(path4, text2) {
26951
- if (existsSync30(path4))
27742
+ if (existsSync32(path4))
26952
27743
  return { path: path4, kind: "file", change: "unchanged" };
26953
27744
  attempt3(path4, "cannot be written", () => {
26954
27745
  writeFileSync11(path4, text2, { encoding: "utf8", flag: "wx" });
@@ -27063,7 +27854,7 @@ var init_scaffold = __esm(() => {
27063
27854
 
27064
27855
  // src/release/enabled.ts
27065
27856
  import { statSync as statSync21 } from "fs";
27066
- import { resolve as resolve19 } from "path";
27857
+ import { resolve as resolve20 } from "path";
27067
27858
  function isRegularFile(full) {
27068
27859
  try {
27069
27860
  return statSync21(full).isFile();
@@ -27072,7 +27863,7 @@ function isRegularFile(full) {
27072
27863
  }
27073
27864
  }
27074
27865
  function readFile5(repoRoot, path4) {
27075
- const resolved = resolve19(repoRoot, path4);
27866
+ const resolved = resolve20(repoRoot, path4);
27076
27867
  return { path: path4, resolved, present: isRegularFile(resolved) };
27077
27868
  }
27078
27869
  function resolveReleaseEnabled(settings, repoRoot) {
@@ -27093,7 +27884,7 @@ var init_enabled = __esm(() => {
27093
27884
  });
27094
27885
 
27095
27886
  // src/release/setting.ts
27096
- import { readFileSync as readFileSync36 } from "fs";
27887
+ import { readFileSync as readFileSync37 } from "fs";
27097
27888
  function releaseEnabledIn(text2, path4) {
27098
27889
  return parseConfigText(text2, path4).values.releaseEnabled ?? null;
27099
27890
  }
@@ -27101,7 +27892,7 @@ function readReleaseSetting(root) {
27101
27892
  const path4 = configFilePath(root);
27102
27893
  let text2;
27103
27894
  try {
27104
- text2 = readFileSync36(path4, "utf8");
27895
+ text2 = readFileSync37(path4, "utf8");
27105
27896
  } catch (error) {
27106
27897
  return { enabled: null, text: "", problem: `${path4} could not be read: ${messageOf(error)}` };
27107
27898
  }
@@ -27281,7 +28072,7 @@ var init_init_release = __esm(() => {
27281
28072
  });
27282
28073
 
27283
28074
  // src/commands/init.ts
27284
- import { existsSync as existsSync31, readFileSync as readFileSync37 } from "fs";
28075
+ import { existsSync as existsSync33, readFileSync as readFileSync38 } from "fs";
27285
28076
  import { homedir as homedir6 } from "os";
27286
28077
  import { join as join45, relative as relative11, sep as sep4 } from "path";
27287
28078
  function refusal5(lines2) {
@@ -27374,7 +28165,7 @@ function resolvedConfig3(root, home, warn) {
27374
28165
  function checkGitignore(root, flags) {
27375
28166
  const file = join45(root, GITIGNORE_FILE);
27376
28167
  try {
27377
- const text2 = existsSync31(file) ? readFileSync37(file, "utf8") : null;
28168
+ const text2 = existsSync33(file) ? readFileSync38(file, "utf8") : null;
27378
28169
  withTrackingBlock(text2, flags, file);
27379
28170
  } catch (error) {
27380
28171
  const message = error instanceof GitignoreError ? error.message : `rafa init: ${file} cannot be read (${messageOf(error)})`;
@@ -27405,8 +28196,8 @@ function initialise(root, start, home, context, entry) {
27405
28196
  checkConflicts(root.path, home);
27406
28197
  const config = resolvedConfig3(root.path, home, warn);
27407
28198
  checkGitignore(root.path, config);
27408
- const configExisted = existsSync31(configFilePath(root.path));
27409
- const digestExisted = existsSync31(join45(root.path, TRACKING_DIGEST_FILE));
28199
+ const configExisted = existsSync33(configFilePath(root.path));
28200
+ const digestExisted = existsSync33(join45(root.path, TRACKING_DIGEST_FILE));
27410
28201
  const project = writeProjectScope(root.path);
27411
28202
  const tracking = applyTracking(root.path, config, warn);
27412
28203
  const writes = [...project, ...trackingWrites(tracking, digestExisted), ...writeUserScope(home)];
@@ -27597,7 +28388,7 @@ var init_init = __esm(() => {
27597
28388
  });
27598
28389
 
27599
28390
  // src/commands/check-report.ts
27600
- import { resolve as resolve20 } from "path";
28391
+ import { resolve as resolve21 } from "path";
27601
28392
  function readCheckDirectory(args, usage, cwd) {
27602
28393
  const [dir, extra] = args;
27603
28394
  if (dir === undefined) {
@@ -27608,7 +28399,7 @@ Usage: ${usage}`);
27608
28399
  throw new CommandExit(1, `\u274C Expected one directory, and read "${extra}" as a second
27609
28400
  Usage: ${usage}`);
27610
28401
  }
27611
- return resolve20(cwd(), dir);
28402
+ return resolve21(cwd(), dir);
27612
28403
  }
27613
28404
  function readFix(value, usage) {
27614
28405
  if (value === undefined || value === false || value === "false")
@@ -27625,7 +28416,7 @@ function readProjectRoot(value, usage, cwd) {
27625
28416
  throw new CommandExit(1, `\u274C --project needs a path, and the line gave it none
27626
28417
  Usage: ${usage}`);
27627
28418
  }
27628
- return resolve20(cwd(), value);
28419
+ return resolve21(cwd(), value);
27629
28420
  }
27630
28421
  function reportMarker(report2) {
27631
28422
  if (report2.failed)
@@ -28297,8 +29088,8 @@ var init_show3 = __esm(() => {
28297
29088
  });
28298
29089
 
28299
29090
  // src/commands/issue/check.ts
28300
- import { readdirSync as readdirSync11, readFileSync as readFileSync38, statSync as statSync22 } from "fs";
28301
- import { join as join46, resolve as resolve21 } from "path";
29091
+ import { readdirSync as readdirSync12, readFileSync as readFileSync39, statSync as statSync22 } from "fs";
29092
+ import { join as join46, resolve as resolve22 } from "path";
28302
29093
  function readCheckIssue(args) {
28303
29094
  if (args.length > 1) {
28304
29095
  throw lineRefusal2(`Expected one issue number, got ${String(args.length)}: ${args.join(" ")}`, CHECK_USAGE);
@@ -28318,7 +29109,7 @@ function missingCopyMessage(issue4, specsDir) {
28318
29109
  function copyNames(dir, issue4) {
28319
29110
  let names;
28320
29111
  try {
28321
- names = readdirSync11(dir);
29112
+ names = readdirSync12(dir);
28322
29113
  } catch (error) {
28323
29114
  if (error instanceof Error && "code" in error && error.code === "ENOENT")
28324
29115
  return [];
@@ -28329,7 +29120,7 @@ function copyNames(dir, issue4) {
28329
29120
  return names.filter((name2) => name2.startsWith(prefix) && name2.endsWith(SPEC_EXTENSION) && name2 !== notes).filter((name2) => name2.length > prefix.length + SPEC_EXTENSION.length).filter((name2) => statSync22(join46(dir, name2)).isFile()).sort();
28330
29121
  }
28331
29122
  function findSavedCopy(root, specsDir, issue4) {
28332
- const names = copyNames(resolve21(root, specsDir), issue4);
29123
+ const names = copyNames(resolve22(root, specsDir), issue4);
28333
29124
  const [only] = names;
28334
29125
  if (only === undefined)
28335
29126
  throw new CommandExit(1, missingCopyMessage(issue4, specsDir));
@@ -28341,7 +29132,7 @@ function findSavedCopy(root, specsDir, issue4) {
28341
29132
  `));
28342
29133
  }
28343
29134
  const path4 = join46(specsDir, only);
28344
- return { path: path4, absolute: resolve21(root, path4) };
29135
+ return { path: path4, absolute: resolve22(root, path4) };
28345
29136
  }
28346
29137
  function liveText(live) {
28347
29138
  return live.kind === "unreadable" ? "unreadable" : fingerprintText(live);
@@ -28389,7 +29180,7 @@ async function readReferences(absolute, issue4, verify, stamp2) {
28389
29180
  return rows2.map((row3) => checkedRef(row3, row3.stamp));
28390
29181
  }
28391
29182
  const once2 = memoiseVerifier(verify);
28392
- const before = await readRefsText({ copy: readFileSync38(absolute, "utf8"), issue: issue4, verify: once2 });
29183
+ const before = await readRefsText({ copy: readFileSync39(absolute, "utf8"), issue: issue4, verify: once2 });
28393
29184
  const after = await restampCopyRefs({ path: absolute, issue: issue4, verify: once2 });
28394
29185
  return before.rows.map((row3, index) => checkedRef(row3, after.rows[index]?.stamp ?? null));
28395
29186
  }
@@ -28541,7 +29332,7 @@ var init_comment = __esm(() => {
28541
29332
 
28542
29333
  // src/effort/store/tracker-refs.ts
28543
29334
  import { randomUUID as randomUUID9 } from "crypto";
28544
- import { existsSync as existsSync32 } from "fs";
29335
+ import { existsSync as existsSync34 } from "fs";
28545
29336
  function refused3(call, reason) {
28546
29337
  return new Error(`effort store: tracker ref ${call} ${reason}; ${REFUSAL_TAILS[call]}`);
28547
29338
  }
@@ -28636,7 +29427,7 @@ function writeTrackerRef(repoRoot, write, seams = {}) {
28636
29427
  wanted.json,
28637
29428
  collectedAt
28638
29429
  ];
28639
- const [action, stored] = withSqliteStore(path4, !existsSync32(path4), (db) => {
29430
+ const [action, stored] = withSqliteStore(path4, !existsSync34(path4), (db) => {
28640
29431
  const place2 = db.transaction(() => placeRef(db, path4, write, wanted, insertValues));
28641
29432
  return place2.immediate();
28642
29433
  });
@@ -28645,7 +29436,7 @@ function writeTrackerRef(repoRoot, write, seams = {}) {
28645
29436
  function readTrackerRef(repoRoot, artifact) {
28646
29437
  checkArtifact2("read", artifact);
28647
29438
  const path4 = sqliteStorePath(repoRoot);
28648
- if (!existsSync32(path4))
29439
+ if (!existsSync34(path4))
28649
29440
  return null;
28650
29441
  const oldest = withSqliteStore(path4, false, (db) => db.query(SELECT_OLDEST).get(artifact));
28651
29442
  return oldest === null ? null : heldRefOf(path4, oldest.seq, oldest.tracker_ref).ref;
@@ -29184,14 +29975,14 @@ var init_roadmap_board = __esm(() => {
29184
29975
  });
29185
29976
 
29186
29977
  // src/board/roadmap-rows.ts
29187
- import { readdirSync as readdirSync12 } from "fs";
29978
+ import { readdirSync as readdirSync13 } from "fs";
29188
29979
  function isMissing2(error) {
29189
29980
  return error instanceof Error && "code" in error && error.code === "ENOENT";
29190
29981
  }
29191
29982
  function createPlanDirNames(dir) {
29192
29983
  return () => {
29193
29984
  try {
29194
- return Object.freeze(readdirSync12(dir));
29985
+ return Object.freeze(readdirSync13(dir));
29195
29986
  } catch (error) {
29196
29987
  if (isMissing2(error))
29197
29988
  return Object.freeze([]);
@@ -29302,7 +30093,7 @@ function viewOf3(issues) {
29302
30093
  known: new Set(issues.map((issue4) => issue4.number))
29303
30094
  };
29304
30095
  }
29305
- function rowOf4(line, board, has, refs) {
30096
+ function rowOf5(line, board, has, refs) {
29306
30097
  const issue4 = board?.byNumber.get(line.issue);
29307
30098
  if (board === null || issue4 === undefined) {
29308
30099
  return Object.freeze({ line, issue: null, spec: null, blocked: null, blockers: Object.freeze([]), has, refs });
@@ -29329,7 +30120,7 @@ async function readRoadmapRows2(options) {
29329
30120
  const refs = await readOrWarn(() => options.refs(issues), new Map, "the saved copies could not be read, so the refs column is empty");
29330
30121
  const board = viewOf3(listed3.issues);
29331
30122
  const sources = { planNames: plans.value, refs: scan2.refs, pulls: pulls.value };
29332
- const rows2 = lines2.map((line) => rowOf4(line, board, readHasColumn(line.issue, sources), refs.value.get(line.issue) ?? null));
30123
+ const rows2 = lines2.map((line) => rowOf5(line, board, readHasColumn(line.issue, sources), refs.value.get(line.issue) ?? null));
29333
30124
  const warnings = [listed3.warning, ...scan2.problems, pulls.warning, plans.warning, refs.warning, ...refsWarnings(refs.value)].filter((warning) => warning !== null);
29334
30125
  return Object.freeze({ roadmap, rows: Object.freeze(rows2), warnings: Object.freeze(warnings) });
29335
30126
  }
@@ -30663,9 +31454,9 @@ var init_run_config = __esm(() => {
30663
31454
  });
30664
31455
 
30665
31456
  // src/start/runtime.ts
30666
- import { existsSync as existsSync33, readdirSync as readdirSync13, realpathSync as realpathSync8, statSync as statSync23 } from "fs";
31457
+ import { existsSync as existsSync35, readdirSync as readdirSync14, realpathSync as realpathSync8, statSync as statSync23 } from "fs";
30667
31458
  import { homedir as homedir7 } from "os";
30668
- import { isAbsolute as isAbsolute13, join as join48, relative as relative13, resolve as resolve22, sep as sep5 } from "path";
31459
+ import { isAbsolute as isAbsolute13, join as join48, relative as relative13, resolve as resolve23, sep as sep5 } from "path";
30669
31460
  function runtimeFlagValue(args) {
30670
31461
  return args.includes(RUNTIME_FLAG) ? "" : argValue(args, RUNTIME_FLAG);
30671
31462
  }
@@ -30687,14 +31478,14 @@ function resolveRuntime(value, places) {
30687
31478
  throw refusal6([`\u274C Refusing to start: \`--runtime\` names no runtime. Spell it ${SPELLING}.`]);
30688
31479
  }
30689
31480
  const isVersion = VERSION_SHAPE.test(value);
30690
- const named = isVersion ? join48(places.home, RUNTIME_SUBDIR, value, RUNTIME_ENTRY) : resolve22(places.cwd, value);
31481
+ const named = isVersion ? join48(places.home, RUNTIME_SUBDIR, value, RUNTIME_ENTRY) : resolve23(places.cwd, value);
30691
31482
  refuseInsideSource(value, named, places);
30692
- const entry = entryOf(value, named, isVersion, places.home);
31483
+ const entry = entryOf2(value, named, isVersion, places.home);
30693
31484
  const real = realpathSync8(entry);
30694
31485
  refuseInsideSource(value, real, places);
30695
31486
  return real;
30696
31487
  }
30697
- function entryOf(value, named, isVersion, home) {
31488
+ function entryOf2(value, named, isVersion, home) {
30698
31489
  if (isVersion) {
30699
31490
  if (isFile3(named))
30700
31491
  return named;
@@ -30726,7 +31517,7 @@ function refuseInsideSource(value, path4, places) {
30726
31517
  }
30727
31518
  function sourceDirs({ cwd, root }) {
30728
31519
  const dirs = [join48(cwd, SOURCE_DIR), join48(root, SOURCE_DIR)];
30729
- const real = dirs.filter((dir) => existsSync33(dir)).map((dir) => realpathSync8(dir));
31520
+ const real = dirs.filter((dir) => existsSync35(dir)).map((dir) => realpathSync8(dir));
30730
31521
  return [...new Set([...dirs, ...real])];
30731
31522
  }
30732
31523
  function isInside3(path4, dir) {
@@ -30735,7 +31526,7 @@ function isInside3(path4, dir) {
30735
31526
  }
30736
31527
  function installedVersions(root) {
30737
31528
  try {
30738
- return readdirSync13(root).filter((name2) => isFile3(join48(root, name2, RUNTIME_ENTRY))).sort((a, b) => a.localeCompare(b));
31529
+ return readdirSync14(root).filter((name2) => isFile3(join48(root, name2, RUNTIME_ENTRY))).sort((a, b) => a.localeCompare(b));
30739
31530
  } catch {
30740
31531
  return [];
30741
31532
  }
@@ -30780,7 +31571,7 @@ function realOrResolved(path4) {
30780
31571
  try {
30781
31572
  return realpathSync8(path4);
30782
31573
  } catch {
30783
- return resolve22(path4);
31574
+ return resolve23(path4);
30784
31575
  }
30785
31576
  }
30786
31577
  async function runRuntime(entry, words, run) {
@@ -30877,7 +31668,7 @@ var init_runtime = __esm(() => {
30877
31668
  });
30878
31669
 
30879
31670
  // src/notices/notices.ts
30880
- import { mkdirSync as mkdirSync13, readFileSync as readFileSync39, writeFileSync as writeFileSync13 } from "fs";
31671
+ import { mkdirSync as mkdirSync13, readFileSync as readFileSync40, writeFileSync as writeFileSync13 } from "fs";
30881
31672
  import { dirname as dirname16, join as join49 } from "path";
30882
31673
  function noticesPath(home) {
30883
31674
  return join49(home, ".rafa", "notices.json");
@@ -30888,7 +31679,7 @@ function isNoticeId(value) {
30888
31679
  function readDismissed(home) {
30889
31680
  let raw;
30890
31681
  try {
30891
- raw = readFileSync39(noticesPath(home), "utf8");
31682
+ raw = readFileSync40(noticesPath(home), "utf8");
30892
31683
  } catch {
30893
31684
  return [];
30894
31685
  }
@@ -31729,7 +32520,7 @@ var init_level = __esm(() => {
31729
32520
  });
31730
32521
 
31731
32522
  // src/release/version.ts
31732
- import { readFileSync as readFileSync40, writeFileSync as writeFileSync14 } from "fs";
32523
+ import { readFileSync as readFileSync41, writeFileSync as writeFileSync14 } from "fs";
31733
32524
  function parseSemanticVersion(raw) {
31734
32525
  const found = VERSION_SHAPE2.exec(raw);
31735
32526
  if (found === null)
@@ -31817,7 +32608,7 @@ function writeManifestVersion(path4, next) {
31817
32608
  const unwritten = { path: path4, written: false, previous: null, version: next };
31818
32609
  let text2;
31819
32610
  try {
31820
- text2 = readFileSync40(path4, "utf8");
32611
+ text2 = readFileSync41(path4, "utf8");
31821
32612
  } catch (error) {
31822
32613
  return { ...unwritten, problem: `${path4} could not be read: ${messageOf(error)}` };
31823
32614
  }
@@ -31872,7 +32663,7 @@ var init_version2 = __esm(() => {
31872
32663
  });
31873
32664
 
31874
32665
  // src/release/prepare.ts
31875
- import { readFileSync as readFileSync41, writeFileSync as writeFileSync15 } from "fs";
32666
+ import { readFileSync as readFileSync42, writeFileSync as writeFileSync15 } from "fs";
31876
32667
  function levelFields(reading2) {
31877
32668
  return { level: reading2.level, levelSource: reading2.source, notesLevel: reading2.notesLevel };
31878
32669
  }
@@ -31928,7 +32719,7 @@ function planVersion(input, versionFile, level) {
31928
32719
  }
31929
32720
  function textOf(path4) {
31930
32721
  try {
31931
- return readFileSync41(path4, "utf8");
32722
+ return readFileSync42(path4, "utf8");
31932
32723
  } catch {
31933
32724
  return null;
31934
32725
  }
@@ -32048,7 +32839,7 @@ var init_prepare = __esm(() => {
32048
32839
  });
32049
32840
 
32050
32841
  // src/release/verify.ts
32051
- import { readFileSync as readFileSync42, writeFileSync as writeFileSync16 } from "fs";
32842
+ import { readFileSync as readFileSync43, writeFileSync as writeFileSync16 } from "fs";
32052
32843
  function changelogInsertionSpan(before, after) {
32053
32844
  const beforeLines = before.split(`
32054
32845
  `);
@@ -32065,7 +32856,7 @@ function changelogInsertionSpan(before, after) {
32065
32856
  }
32066
32857
  function textOf2(path4) {
32067
32858
  try {
32068
- return readFileSync42(path4, "utf8");
32859
+ return readFileSync43(path4, "utf8");
32069
32860
  } catch {
32070
32861
  return null;
32071
32862
  }
@@ -32465,9 +33256,9 @@ function describeStartTriage(result) {
32465
33256
  };
32466
33257
  }
32467
33258
  async function resolvePublicTracker(options, redact) {
32468
- const resolve23 = options.resolve ?? resolveTracker;
33259
+ const resolve24 = options.resolve ?? resolveTracker;
32469
33260
  try {
32470
- const { tracker, attempts } = await resolve23({
33261
+ const { tracker, attempts } = await resolve24({
32471
33262
  config: options.config,
32472
33263
  context: { repoRoot: options.repoRoot }
32473
33264
  });
@@ -32877,7 +33668,7 @@ async function openPullRequestNumber(branch) {
32877
33668
  return null;
32878
33669
  }
32879
33670
  }
32880
- function messageOf12(error) {
33671
+ function messageOf13(error) {
32881
33672
  return error instanceof Error ? error.message : String(error);
32882
33673
  }
32883
33674
  function wrapUpAdapter(learning) {
@@ -32898,7 +33689,7 @@ async function lessonsToPromote(learning) {
32898
33689
  minConfidence: learning.promoteMinConfidence
32899
33690
  });
32900
33691
  } catch (error) {
32901
- activeOutput().warn(` The wrap-up lists no lesson to promote: the \`${learning.kind}\` learning adapter answered no blessed set: ${messageOf12(error)}`);
33692
+ activeOutput().warn(` The wrap-up lists no lesson to promote: the \`${learning.kind}\` learning adapter answered no blessed set: ${messageOf13(error)}`);
32902
33693
  return [];
32903
33694
  }
32904
33695
  }
@@ -32964,8 +33755,8 @@ function msUntil(startAt, now = new Date) {
32964
33755
  return target.getTime() - now.getTime();
32965
33756
  }
32966
33757
  function sleepFor(ms) {
32967
- return new Promise((resolve23) => {
32968
- setTimeout(resolve23, ms);
33758
+ return new Promise((resolve24) => {
33759
+ setTimeout(resolve24, ms);
32969
33760
  });
32970
33761
  }
32971
33762
  async function deferUntil(startAt, now = new Date, sleep = sleepFor) {
@@ -34459,8 +35250,8 @@ var init_create2 = __esm(() => {
34459
35250
  });
34460
35251
 
34461
35252
  // src/commands/plan/risk.ts
34462
- import { readFileSync as readFileSync43 } from "fs";
34463
- import { resolve as resolve23 } from "path";
35253
+ import { readFileSync as readFileSync44 } from "fs";
35254
+ import { resolve as resolve24 } from "path";
34464
35255
  function spawningRunners2(root) {
34465
35256
  return { git: createGitRunner(root), gh: createGhRunner({ cwd: root }) };
34466
35257
  }
@@ -34470,7 +35261,7 @@ function riskPlanPath(root, planDir, typed) {
34470
35261
  return path7;
34471
35262
  if (typed !== null)
34472
35263
  throw new CommandExit(1, `\u274C Plan file not found: ${path7}`);
34473
- const lookedFor = [...new Set([resolve23(root, planDir, DEFAULT_PLAN_FILE), resolve23(root, DEFAULT_PLAN_FILE)])];
35264
+ const lookedFor = [...new Set([resolve24(root, planDir, DEFAULT_PLAN_FILE), resolve24(root, DEFAULT_PLAN_FILE)])];
34474
35265
  throw new CommandExit(1, `\u274C No plan named, and no default plan at ${lookedFor.join(" or ")}
34475
35266
  Usage: ${USAGE24}`);
34476
35267
  }
@@ -34484,7 +35275,7 @@ function strictRefusal(report2) {
34484
35275
  }
34485
35276
  async function readRisk(path7, project, config, context, seams) {
34486
35277
  const makeRunners = seams.runners ?? spawningRunners2;
34487
- return assessPlanRisk({ path: path7, text: readFileSync43(path7, "utf8") }, {
35278
+ return assessPlanRisk({ path: path7, text: readFileSync44(path7, "utf8") }, {
34488
35279
  repoRoot: project.root,
34489
35280
  home: project.home,
34490
35281
  settingSources: config.settingSources,
@@ -34577,7 +35368,7 @@ var init_risk2 = __esm(() => {
34577
35368
  });
34578
35369
 
34579
35370
  // src/commands/plan/show.ts
34580
- import { readFileSync as readFileSync44 } from "fs";
35371
+ import { readFileSync as readFileSync45 } from "fs";
34581
35372
  import { join as join50 } from "path";
34582
35373
  function readTrackerFlag(value) {
34583
35374
  if (value === undefined || value === false || value === "false")
@@ -34597,7 +35388,7 @@ function showPlan(plans, stub, tracker) {
34597
35388
  const file = join50(plans.path, name2);
34598
35389
  if (!isFile2(file))
34599
35390
  throw new CommandExit(1, missingText(stub, tracker, join50(plans.label, name2)));
34600
- const model = parsePlan(readFileSync44(file, "utf8"));
35391
+ const model = parsePlan(readFileSync45(file, "utf8"));
34601
35392
  const { header } = model;
34602
35393
  return {
34603
35394
  stub,
@@ -34771,6 +35562,7 @@ function openPrContext(context, seams = DEFAULT_PR_SEAMS) {
34771
35562
  resolveBudget: config.prResolveBudget,
34772
35563
  trustedAuthors: config.boardTrustedAuthors,
34773
35564
  roadmapIssue: config.roadmapIssue,
35565
+ planDir: config.planDir,
34774
35566
  readBranch: () => readBranch2(project.root)
34775
35567
  };
34776
35568
  }
@@ -34992,7 +35784,7 @@ async function probe(call) {
34992
35784
  return { value: null, problem: messageOf(error) };
34993
35785
  }
34994
35786
  }
34995
- async function rowOf5(pulls, pull) {
35787
+ async function rowOf6(pulls, pull) {
34996
35788
  const [checks, detail] = await Promise.all([
34997
35789
  probe(() => pulls.checks(pull.number)),
34998
35790
  probe(() => pulls.get(pull.number))
@@ -35010,7 +35802,7 @@ async function readList2(context, seams, now) {
35010
35802
  expectNoArguments(context.args, USAGE27);
35011
35803
  const pr = openPrContext(context, seams);
35012
35804
  const open = await onProvider("list the open pull requests", () => pr.pulls.list());
35013
- const rows2 = await mapWithLimit(open, PROBE_LIMIT, (pull) => rowOf5(pr.pulls, pull));
35805
+ const rows2 = await mapWithLimit(open, PROBE_LIMIT, (pull) => rowOf6(pr.pulls, pull));
35014
35806
  return { rows: rows2, text: renderList(rows2, now) };
35015
35807
  }
35016
35808
  function createPrListCommand(seams = DEFAULT_PR_SEAMS) {
@@ -35209,17 +36001,17 @@ var init_roadmap_tick = __esm(() => {
35209
36001
  import {
35210
36002
  chmodSync,
35211
36003
  copyFileSync as copyFileSync3,
35212
- existsSync as existsSync34,
36004
+ existsSync as existsSync36,
35213
36005
  mkdirSync as mkdirSync14,
35214
- readdirSync as readdirSync14,
35215
- readFileSync as readFileSync45,
36006
+ readdirSync as readdirSync15,
36007
+ readFileSync as readFileSync46,
35216
36008
  realpathSync as realpathSync9,
35217
36009
  renameSync as renameSync4,
35218
36010
  rmSync as rmSync5,
35219
36011
  statSync as statSync24,
35220
36012
  symlinkSync
35221
36013
  } from "fs";
35222
- import { basename as basename16, delimiter as delimiter4, dirname as dirname17, join as join51, relative as relative15, resolve as resolve24 } from "path";
36014
+ import { basename as basename16, delimiter as delimiter4, dirname as dirname17, join as join51, relative as relative15, resolve as resolve25 } from "path";
35223
36015
  function exitCodeFor(outcome2) {
35224
36016
  switch (outcome2.kind) {
35225
36017
  case "done":
@@ -35257,17 +36049,17 @@ function runtimeRefusalLines(runtimeDir, version2) {
35257
36049
  ];
35258
36050
  }
35259
36051
  function listTrackers(repoRoot, planDir) {
35260
- const dir = resolve24(repoRoot, planDir);
36052
+ const dir = resolve25(repoRoot, planDir);
35261
36053
  return entryNames(dir).filter((entry) => TRACKER_NAME.test(entry)).sort((a, b) => a.localeCompare(b)).map((entry) => join51(dir, entry)).filter((path7) => statSync24(path7).isFile()).map((path7) => relative15(repoRoot, path7));
35262
36054
  }
35263
36055
  function openTrackers(repoRoot, planDir) {
35264
36056
  return listTrackers(repoRoot, planDir).flatMap((path7) => {
35265
- const task = findNextTask(readFileSync45(join51(repoRoot, path7), "utf8"));
36057
+ const task = findNextTask(readFileSync46(join51(repoRoot, path7), "utf8"));
35266
36058
  return task ? [{ path: path7, task }] : [];
35267
36059
  });
35268
36060
  }
35269
36061
  function readManifestVersion2(repoRoot) {
35270
- const manifest = JSON.parse(readFileSync45(join51(repoRoot, "package.json"), "utf8"));
36062
+ const manifest = JSON.parse(readFileSync46(join51(repoRoot, "package.json"), "utf8"));
35271
36063
  const record = typeof manifest === "object" && manifest !== null ? manifest : {};
35272
36064
  if (record["name"] !== name) {
35273
36065
  throw new Error(`${join51(repoRoot, "package.json")} names ${JSON.stringify(record["name"])}, not ${name}: this is no rafa checkout`);
@@ -35281,7 +36073,7 @@ function readManifestVersion2(repoRoot) {
35281
36073
  function copyTree(from, to) {
35282
36074
  mkdirSync14(to, { recursive: true });
35283
36075
  let copied = 0;
35284
- const entries = readdirSync14(from, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name));
36076
+ const entries = readdirSync15(from, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name));
35285
36077
  for (const entry of entries) {
35286
36078
  const source = join51(from, entry.name);
35287
36079
  const target = join51(to, entry.name);
@@ -35309,7 +36101,7 @@ function replaceTree(from, to) {
35309
36101
  }
35310
36102
  }
35311
36103
  function swapIn2(staging, to) {
35312
- if (!existsSync34(to)) {
36104
+ if (!existsSync36(to)) {
35313
36105
  renameSync4(staging, to);
35314
36106
  return;
35315
36107
  }
@@ -35385,14 +36177,14 @@ function runInstall(seams, options) {
35385
36177
  log(`runtime root: ${runtimeRoot}`);
35386
36178
  log(`link: ${linkPath}`);
35387
36179
  const version2 = inStage("manifest", () => readManifestVersion2(repoRoot));
35388
- const planDir = inStage("config", () => resolve24(repoRoot, loadConfig({ root: repoRoot, home }, {}, seams.warn).config.planDir));
36180
+ const planDir = inStage("config", () => resolve25(repoRoot, loadConfig({ root: repoRoot, home }, {}, seams.warn).config.planDir));
35389
36181
  log(`plan dir: ${planDir}`);
35390
36182
  const open = inStage("trackers", () => openTrackers(repoRoot, planDir));
35391
36183
  if (open.length > 0)
35392
36184
  return { kind: "refused", reason: "trackers", planDir, trackers: open };
35393
36185
  const distDir = join51(repoRoot, "dist");
35394
36186
  const runtimeDir = join51(runtimeRoot, version2);
35395
- const installed = existsSync34(runtimeDir);
36187
+ const installed = existsSync36(runtimeDir);
35396
36188
  if (installed && options.force !== true) {
35397
36189
  return { kind: "refused", reason: "runtime-exists", version: version2, runtimeDir };
35398
36190
  }
@@ -35400,7 +36192,7 @@ function runInstall(seams, options) {
35400
36192
  const code = inStage("build", () => seams.build(repoRoot));
35401
36193
  if (code !== 0)
35402
36194
  throw new StageFailure("build", `the build exited ${code}`);
35403
- if (!existsSync34(join51(distDir, RUNTIME_ENTRY))) {
36195
+ if (!existsSync36(join51(distDir, RUNTIME_ENTRY))) {
35404
36196
  throw new StageFailure("build", `the build exited 0 and left no ${join51(distDir, RUNTIME_ENTRY)}`);
35405
36197
  }
35406
36198
  log(installed ? `copying ${distDir} over ${runtimeDir}, replacing it whole` : `copying ${distDir} into ${runtimeDir}`);
@@ -35421,7 +36213,7 @@ function inStage(stage, run) {
35421
36213
  }
35422
36214
  function entryNames(dir) {
35423
36215
  try {
35424
- return readdirSync14(dir);
36216
+ return readdirSync15(dir);
35425
36217
  } catch (err) {
35426
36218
  if (typeof err === "object" && err !== null && "code" in err && err.code === "ENOENT")
35427
36219
  return [];
@@ -35692,7 +36484,7 @@ var init_merge_unchecked = __esm(() => {
35692
36484
  });
35693
36485
 
35694
36486
  // src/commands/pr/merge.ts
35695
- import { existsSync as existsSync35, readFileSync as readFileSync46 } from "fs";
36487
+ import { existsSync as existsSync37, readFileSync as readFileSync47 } from "fs";
35696
36488
  import { join as join52 } from "path";
35697
36489
  function refusal8(lines2) {
35698
36490
  return new CommandExit(1, lines2.join(`
@@ -35770,7 +36562,7 @@ function remoteHoldsBranch(git, branch, warn) {
35770
36562
  }
35771
36563
  function textOf3(path7) {
35772
36564
  try {
35773
- return readFileSync46(path7, "utf8");
36565
+ return readFileSync47(path7, "utf8");
35774
36566
  } catch {
35775
36567
  return "";
35776
36568
  }
@@ -35784,7 +36576,7 @@ function followUpsFor(pr, git) {
35784
36576
  version: facts.version,
35785
36577
  tagged: tags.ok && tags.stdout.trim() !== "",
35786
36578
  rafaCheckout: facts.rafaCheckout,
35787
- runtimeInstalled: existsSync35(join52(pr.project.home, RUNTIME_SUBDIR, facts.version))
36579
+ runtimeInstalled: existsSync37(join52(pr.project.home, RUNTIME_SUBDIR, facts.version))
35788
36580
  });
35789
36581
  }
35790
36582
  function openGh(pr, seams) {
@@ -35909,6 +36701,15 @@ async function runMerge(context, seams) {
35909
36701
  throw lineRefusal4(`No pull request #${pick.number} at ${pr.project.root}`, USAGE28);
35910
36702
  }
35911
36703
  const checks = await onProvider(`read the checks of #${pick.number}`, () => pr.pulls.checks(pick.number));
36704
+ recordPlanCi({
36705
+ repoRoot: pr.project.root,
36706
+ planDir: pr.planDir,
36707
+ pullRequest: detail,
36708
+ rows: checks.rows,
36709
+ warn: (message) => {
36710
+ context.output.warn(message);
36711
+ }
36712
+ });
35912
36713
  refuseFromGit(git, detail, checks, skipChecks);
35913
36714
  const summary = summaryLine2(detail, method);
35914
36715
  const answer4 = await askToMerge(context, pr, seams, { number: detail.number, summary, yes, skipChecks });
@@ -36030,6 +36831,7 @@ var init_merge3 = __esm(() => {
36030
36831
  init_roadmap_tick();
36031
36832
  init_command();
36032
36833
  init_confirm();
36834
+ init_plan_ci();
36033
36835
  init_ending();
36034
36836
  init_pr();
36035
36837
  init_runtime();
@@ -37010,8 +37812,8 @@ function describeAge(updatedAt, now) {
37010
37812
  return `${plural6(hours, "hour", "hours")} ago`;
37011
37813
  return `${plural6(Math.floor(age / DAY_MS2), "day", "days")} ago`;
37012
37814
  }
37013
- function triageCommand(number, resolve25 = false) {
37014
- return resolve25 ? `rafa pr triage ${number} --resolve` : `rafa pr triage ${number}`;
37815
+ function triageCommand(number, resolve26 = false) {
37816
+ return resolve26 ? `rafa pr triage ${number} --resolve` : `rafa pr triage ${number}`;
37015
37817
  }
37016
37818
  function joinNumbers(pulls) {
37017
37819
  const names = pulls.map((pull) => `#${pull.number}`);
@@ -37022,14 +37824,14 @@ function joinNumbers(pulls) {
37022
37824
  function byNumber(pulls) {
37023
37825
  return Object.freeze([...pulls].sort((left, right) => left.number - right.number));
37024
37826
  }
37025
- function skipped2(pull, now, resolve25) {
37827
+ function skipped2(pull, now, resolve26) {
37026
37828
  return Object.freeze({
37027
37829
  number: pull.number,
37028
37830
  title: pull.title,
37029
37831
  updatedAt: pull.updatedAt,
37030
37832
  age: describeAge(pull.updatedAt, now),
37031
37833
  fresh: isFreshCandidate(pull.updatedAt, now),
37032
- command: triageCommand(pull.number, resolve25)
37834
+ command: triageCommand(pull.number, resolve26)
37033
37835
  });
37034
37836
  }
37035
37837
  function skippedLine(one) {
@@ -37073,15 +37875,15 @@ function listReading(rest, count2) {
37073
37875
  return listing2("list", Object.freeze([]), rest, headline);
37074
37876
  }
37075
37877
  function selectTriagePullRequests(input) {
37076
- const resolve25 = input.resolve === true;
37878
+ const resolve26 = input.resolve === true;
37077
37879
  const { current, now } = input;
37078
37880
  if (current !== null) {
37079
37881
  return plain("branch", Object.freeze([current]), `assessing #${current.number}, the pull request open on ${current.headRefName}`);
37080
37882
  }
37081
37883
  const candidates = byNumber(input.candidates);
37082
37884
  const count2 = candidates.length;
37083
- const lines2 = candidates.map((pull) => skipped2(pull, now, resolve25));
37084
- if (resolve25 && count2 > 1)
37885
+ const lines2 = candidates.map((pull) => skipped2(pull, now, resolve26));
37886
+ if (resolve26 && count2 > 1)
37085
37887
  return refusal9(lines2, count2);
37086
37888
  if (count2 === 0) {
37087
37889
  return plain("none", Object.freeze([]), "no red pull request to triage, and no pull request on the current branch");
@@ -37442,7 +38244,7 @@ var init_budget2 = __esm(() => {
37442
38244
  });
37443
38245
 
37444
38246
  // src/pr/plans/load.ts
37445
- import { existsSync as existsSync36, readFileSync as readFileSync47 } from "fs";
38247
+ import { existsSync as existsSync38, readFileSync as readFileSync48 } from "fs";
37446
38248
  import { dirname as dirname18, join as join53 } from "path";
37447
38249
  import { fileURLToPath as fileURLToPath4 } from "url";
37448
38250
  function pinnedPlanFileName(triageClass) {
@@ -37463,11 +38265,11 @@ function readPinnedPlan(triageClass, lookup = {}) {
37463
38265
  throw new Error(`No pinned resolve plan ships for ${triageClass}; ` + `one ships for ${PINNED_PLAN_CLASSES.join(", ")}`);
37464
38266
  }
37465
38267
  const candidates = pinnedPlanCandidates(lookup.moduleDir ?? MODULE_DIR2, triageClass);
37466
- const found = candidates.find((candidate) => existsSync36(candidate));
38268
+ const found = candidates.find((candidate) => existsSync38(candidate));
37467
38269
  if (found === undefined) {
37468
38270
  throw new Error(`The pinned resolve plan for ${triageClass} is missing: ` + `no file at ${candidates.join(" or ")}`);
37469
38271
  }
37470
- return readFileSync47(found, "utf8");
38272
+ return readFileSync48(found, "utf8");
37471
38273
  }
37472
38274
  function fillPinnedPlan(template, values) {
37473
38275
  const missing2 = [];
@@ -38044,16 +38846,16 @@ async function redPullRequests(pr) {
38044
38846
  }
38045
38847
  return red;
38046
38848
  }
38047
- async function selectTargets(pr, warn, now, resolve25) {
38849
+ async function selectTargets(pr, warn, now, resolve26) {
38048
38850
  const branch = readBranchOrNone(pr, warn);
38049
38851
  const current = branch === null ? null : await onProvider(`read the open pull request for the branch "${branch}"`, () => pr.pulls.findOpen(branch));
38050
38852
  if (current !== null)
38051
- return selectTriagePullRequests({ current, candidates: [], now, resolve: resolve25 });
38853
+ return selectTriagePullRequests({ current, candidates: [], now, resolve: resolve26 });
38052
38854
  return selectTriagePullRequests({
38053
38855
  current: null,
38054
38856
  candidates: await redPullRequests(pr),
38055
38857
  now,
38056
- resolve: resolve25
38858
+ resolve: resolve26
38057
38859
  });
38058
38860
  }
38059
38861
  async function detailOf12(pr, number) {
@@ -38115,6 +38917,14 @@ async function assessOne(options) {
38115
38917
  const { git, maxAttempts, number, pr } = options;
38116
38918
  const detail = await detailOf12(pr, number);
38117
38919
  const checks = await onProvider(`read the checks of #${number}`, () => pr.pulls.checks(number));
38920
+ recordPlanCi({
38921
+ repoRoot: pr.project.root,
38922
+ planDir: pr.planDir,
38923
+ pullRequest: detail,
38924
+ rows: checks.rows,
38925
+ warn: options.warn,
38926
+ now: () => new Date(options.at)
38927
+ });
38118
38928
  const found = await triageCommentOf(options, detail);
38119
38929
  const existing = found.comment;
38120
38930
  const rerun = readTriageRerun({
@@ -38174,7 +38984,7 @@ async function runTriage(context, seams) {
38174
38984
  }
38175
38985
  const numbers = asked === null ? (selection?.assess ?? []).map((one) => one.number) : [asked];
38176
38986
  const readings = [];
38177
- let resolve25 = null;
38987
+ let resolve26 = null;
38178
38988
  for (const number of numbers) {
38179
38989
  const assess = () => assessOne({
38180
38990
  pr,
@@ -38183,14 +38993,17 @@ async function runTriage(context, seams) {
38183
38993
  wantsComment,
38184
38994
  maxAttempts,
38185
38995
  at: now(),
38186
- permissions
38996
+ permissions,
38997
+ warn: (message) => {
38998
+ context.output.warn(message);
38999
+ }
38187
39000
  });
38188
39001
  const reading2 = await assess();
38189
39002
  if (!wantsResolve) {
38190
39003
  readings.push(reading2);
38191
39004
  continue;
38192
39005
  }
38193
- resolve25 = await resolvePullRequest({
39006
+ resolve26 = await resolvePullRequest({
38194
39007
  trust: {
38195
39008
  permissions,
38196
39009
  trustedAuthors: pr.trustedAuthors,
@@ -38211,16 +39024,16 @@ async function runTriage(context, seams) {
38211
39024
  ...seams.clock === undefined ? {} : { clock: seams.clock },
38212
39025
  ...seams.sleep === undefined ? {} : { sleep: seams.sleep }
38213
39026
  });
38214
- readings.push(resolve25.reading);
39027
+ readings.push(resolve26.reading);
38215
39028
  }
38216
39029
  const text2 = [
38217
39030
  ...selection === null ? [] : [selection.message],
38218
39031
  ...readings.length === 0 ? [] : [renderTriages(readings)],
38219
- ...resolve25 === null ? [] : resolve25.lines
39032
+ ...resolve26 === null ? [] : resolve26.lines
38220
39033
  ].join(`
38221
39034
 
38222
39035
  `);
38223
- return { selection, readings, resolve: resolve25, text: text2, exitCode: resolve25?.exitCode ?? 0 };
39036
+ return { selection, readings, resolve: resolve26, text: text2, exitCode: resolve26?.exitCode ?? 0 };
38224
39037
  }
38225
39038
  function createPrTriageCommand(seams = DEFAULT_TRIAGE_SEAMS) {
38226
39039
  const command = {
@@ -38295,6 +39108,7 @@ var USAGE30, DEFAULT_MAX_ATTEMPTS = 2, DETACHED_HEAD2 = "HEAD", RESOLVED = false
38295
39108
  var init_triage4 = __esm(() => {
38296
39109
  init_command();
38297
39110
  init_config_sections();
39111
+ init_plan_ci();
38298
39112
  init_ending();
38299
39113
  init_pr();
38300
39114
  init_classify3();
@@ -38539,7 +39353,7 @@ var init_wait = __esm(() => {
38539
39353
  });
38540
39354
 
38541
39355
  // src/commands/release/status.ts
38542
- import { existsSync as existsSync37, readdirSync as readdirSync15, readFileSync as readFileSync48 } from "fs";
39356
+ import { existsSync as existsSync39, readdirSync as readdirSync16, readFileSync as readFileSync49 } from "fs";
38543
39357
  import { join as join55 } from "path";
38544
39358
  function compareIdentifiers(left, right) {
38545
39359
  const leftNumeric = NUMERIC_IDENTIFIER.test(left);
@@ -38630,7 +39444,7 @@ function untaggedVersions(released, tags) {
38630
39444
  }
38631
39445
  function textOf4(path7) {
38632
39446
  try {
38633
- return readFileSync48(path7, "utf8");
39447
+ return readFileSync49(path7, "utf8");
38634
39448
  } catch {
38635
39449
  return null;
38636
39450
  }
@@ -38671,12 +39485,12 @@ function readBranch2(git) {
38671
39485
  const branch = result.stdout.trim();
38672
39486
  return result.ok && branch !== "" && branch !== "HEAD" ? branch : null;
38673
39487
  }
38674
- function planStubsUnder(root, planDir) {
39488
+ function planStubsUnder2(root, planDir) {
38675
39489
  const resolved = join55(root, planDir);
38676
- if (!existsSync37(resolved))
39490
+ if (!existsSync39(resolved))
38677
39491
  return [];
38678
39492
  try {
38679
- return planStubsFromFileNames(readdirSync15(resolved));
39493
+ return planStubsFromFileNames(readdirSync16(resolved));
38680
39494
  } catch {
38681
39495
  return [];
38682
39496
  }
@@ -38829,7 +39643,7 @@ function readStatus(context, seams = DEFAULT_RELEASE_SEAMS) {
38829
39643
  versionFile: readVersionFile(project.root, config.versionFile),
38830
39644
  tags,
38831
39645
  untagged: readUntagged(project.root, config.changelog, tags.tags),
38832
- plan: readPending(project.root, asked, readBranch2(git), planStubsUnder(project.root, config.planDir), readNotes)
39646
+ plan: readPending(project.root, asked, readBranch2(git), planStubsUnder2(project.root, config.planDir), readNotes)
38833
39647
  };
38834
39648
  return { reading: reading2, text: renderStatus2(reading2) };
38835
39649
  }
@@ -38893,7 +39707,7 @@ var init_status3 = __esm(() => {
38893
39707
  });
38894
39708
 
38895
39709
  // src/commands/release/tag.ts
38896
- import { readFileSync as readFileSync49 } from "fs";
39710
+ import { readFileSync as readFileSync50 } from "fs";
38897
39711
  import { join as join56 } from "path";
38898
39712
  function readBranch3(git) {
38899
39713
  const result = git(["rev-parse", "--abbrev-ref", "HEAD"]);
@@ -38908,7 +39722,7 @@ function readBranch3(git) {
38908
39722
  }
38909
39723
  function textOf5(path7) {
38910
39724
  try {
38911
- return readFileSync49(path7, "utf8");
39725
+ return readFileSync50(path7, "utf8");
38912
39726
  } catch {
38913
39727
  return null;
38914
39728
  }
@@ -39244,7 +40058,7 @@ var init_self_update = __esm(() => {
39244
40058
  });
39245
40059
 
39246
40060
  // src/backfill/proposal-file.ts
39247
- import { readdirSync as readdirSync16, readFileSync as readFileSync50 } from "fs";
40061
+ import { readdirSync as readdirSync17, readFileSync as readFileSync51 } from "fs";
39248
40062
  import { join as join57 } from "path";
39249
40063
  function proposalPath(dir, batch) {
39250
40064
  return join57(dir, `${PROPOSAL_FILE_PREFIX}${String(batch).padStart(2, "0")}${PROPOSAL_FILE_EXTENSION}`);
@@ -39378,14 +40192,14 @@ function parseProposalFile(text2) {
39378
40192
  function readProposalFiles(dir) {
39379
40193
  let names;
39380
40194
  try {
39381
- names = readdirSync16(dir);
40195
+ names = readdirSync17(dir);
39382
40196
  } catch {
39383
40197
  return [];
39384
40198
  }
39385
40199
  return names.filter((name2) => isProposalFileName(name2)).sort((left, right) => left.localeCompare(right)).map((name2) => {
39386
40200
  const path7 = join57(dir, name2);
39387
40201
  try {
39388
- return { path: path7, result: parseProposalFile(readFileSync50(path7, "utf8")) };
40202
+ return { path: path7, result: parseProposalFile(readFileSync51(path7, "utf8")) };
39389
40203
  } catch (error) {
39390
40204
  return { path: path7, result: unreadable2(`the file cannot be read (${error instanceof Error ? error.message : String(error)})`) };
39391
40205
  }
@@ -39416,19 +40230,19 @@ var init_proposal_file = __esm(() => {
39416
40230
  });
39417
40231
 
39418
40232
  // src/backfill/backup.ts
39419
- import { copyFileSync as copyFileSync4, existsSync as existsSync38, mkdirSync as mkdirSync16 } from "fs";
39420
- import { dirname as dirname19, isAbsolute as isAbsolute15, join as join58, relative as relative16, resolve as resolve25, sep as sep6 } from "path";
40233
+ import { copyFileSync as copyFileSync4, existsSync as existsSync40, mkdirSync as mkdirSync16 } from "fs";
40234
+ import { dirname as dirname19, isAbsolute as isAbsolute15, join as join58, relative as relative16, resolve as resolve26, sep as sep6 } from "path";
39421
40235
  function backupDirectory(base) {
39422
40236
  return join58(base, BACKUP_PATH);
39423
40237
  }
39424
40238
  function isUnder(root, path7) {
39425
- const step2 = relative16(resolve25(root), resolve25(path7));
40239
+ const step2 = relative16(resolve26(root), resolve26(path7));
39426
40240
  return step2 === "" || !step2.startsWith(`..${sep6}`) && step2 !== ".." && !isAbsolute15(step2);
39427
40241
  }
39428
40242
  function backupPathFor(path7, root, backupDir) {
39429
40243
  if (!isUnder(root, path7))
39430
40244
  return null;
39431
- return join58(backupDir, relative16(resolve25(root), resolve25(path7)));
40245
+ return join58(backupDir, relative16(resolve26(root), resolve26(path7)));
39432
40246
  }
39433
40247
  function backupFile(path7, options) {
39434
40248
  if (options.repoRoot !== null && isUnder(options.repoRoot, path7)) {
@@ -39437,9 +40251,9 @@ function backupFile(path7, options) {
39437
40251
  const backup = backupPathFor(path7, options.root, options.backupDir);
39438
40252
  if (backup === null)
39439
40253
  return { path: path7, kind: "outside", backup: null };
39440
- if (!existsSync38(path7))
40254
+ if (!existsSync40(path7))
39441
40255
  return { path: path7, kind: "missing", backup: null };
39442
- if (existsSync38(backup))
40256
+ if (existsSync40(backup))
39443
40257
  return { path: path7, kind: "kept", backup };
39444
40258
  mkdirSync16(dirname19(backup), { recursive: true });
39445
40259
  copyFileSync4(path7, backup);
@@ -39470,7 +40284,7 @@ var init_backup = __esm(() => {
39470
40284
  });
39471
40285
 
39472
40286
  // src/backfill/derive.ts
39473
- import { readFileSync as readFileSync51, writeFileSync as writeFileSync18 } from "fs";
40287
+ import { readFileSync as readFileSync52, writeFileSync as writeFileSync18 } from "fs";
39474
40288
  import { basename as basename17 } from "path";
39475
40289
  function renderWhenToUse(trigger, prevents) {
39476
40290
  return `${sentenceEnded(trigger)} Prevents: ${prevents}`;
@@ -39589,7 +40403,7 @@ function planFile(entry, options) {
39589
40403
  }
39590
40404
  let original;
39591
40405
  try {
39592
- original = readFileSync51(entry.path, "utf8");
40406
+ original = readFileSync52(entry.path, "utf8");
39593
40407
  } catch (error) {
39594
40408
  return inert(entry, "skipped", `the file cannot be read (${error instanceof Error ? error.message : String(error)})`);
39595
40409
  }
@@ -39633,7 +40447,7 @@ function planDerivation(root, options) {
39633
40447
  function applyFile(action, options) {
39634
40448
  if (action.text === null || action.original === null)
39635
40449
  return action;
39636
- const current = readFileSync51(action.path, "utf8");
40450
+ const current = readFileSync52(action.path, "utf8");
39637
40451
  if (current !== action.original) {
39638
40452
  return {
39639
40453
  ...action,
@@ -39911,7 +40725,7 @@ var init_report3 = __esm(() => {
39911
40725
  });
39912
40726
 
39913
40727
  // src/backfill/proposal-batch.ts
39914
- import { readFileSync as readFileSync52 } from "fs";
40728
+ import { readFileSync as readFileSync53 } from "fs";
39915
40729
  import { relative as relative17 } from "path";
39916
40730
  function candidateNeeds(data, body) {
39917
40731
  const needs = [];
@@ -39936,7 +40750,7 @@ function readCandidate(root, entry) {
39936
40750
  return null;
39937
40751
  let text2;
39938
40752
  try {
39939
- text2 = readFileSync52(entry.path, "utf8");
40753
+ text2 = readFileSync53(entry.path, "utf8");
39940
40754
  } catch {
39941
40755
  return null;
39942
40756
  }
@@ -40086,8 +40900,8 @@ The files:
40086
40900
  });
40087
40901
 
40088
40902
  // src/backfill/propose.ts
40089
- import { mkdirSync as mkdirSync17, readFileSync as readFileSync53, writeFileSync as writeFileSync19 } from "fs";
40090
- import { join as join59, resolve as resolve26 } from "path";
40903
+ import { mkdirSync as mkdirSync17, readFileSync as readFileSync54, writeFileSync as writeFileSync19 } from "fs";
40904
+ import { join as join59, resolve as resolve27 } from "path";
40091
40905
  function unanswered(candidate, note) {
40092
40906
  return {
40093
40907
  path: candidate.relative,
@@ -40239,7 +41053,7 @@ function applyRow(root, row3, options) {
40239
41053
  }
40240
41054
  let original;
40241
41055
  try {
40242
- original = readFileSync53(path7, "utf8");
41056
+ original = readFileSync54(path7, "utf8");
40243
41057
  } catch {
40244
41058
  return inert2(path7, row3, "refused", "the file the row names is gone");
40245
41059
  }
@@ -40253,7 +41067,7 @@ function fileRefusal(file, root) {
40253
41067
  if (file.status !== REVIEWED_FILE) {
40254
41068
  return `the proposal file is still marked ${file.status}: review it and mark it ${REVIEWED_FILE}`;
40255
41069
  }
40256
- return resolve26(file.skills) === resolve26(root) ? null : `the proposal file names the skills directory ${file.skills}, not ${root}`;
41070
+ return resolve27(file.skills) === resolve27(root) ? null : `the proposal file names the skills directory ${file.skills}, not ${root}`;
40257
41071
  }
40258
41072
  function countProposalActions(actions) {
40259
41073
  const counts = Object.fromEntries(PROPOSAL_ACTION_KINDS.map((kind) => [kind, 0]));
@@ -40294,7 +41108,7 @@ var init_propose = __esm(() => {
40294
41108
  });
40295
41109
 
40296
41110
  // src/demote/select.ts
40297
- import { readdirSync as readdirSync17, readFileSync as readFileSync54, realpathSync as realpathSync10, statSync as statSync25 } from "fs";
41111
+ import { readdirSync as readdirSync18, readFileSync as readFileSync55, realpathSync as realpathSync10, statSync as statSync25 } from "fs";
40298
41112
  import { basename as basename18, dirname as dirname20, join as join60 } from "path";
40299
41113
  function realOrSelf(path7) {
40300
41114
  try {
@@ -40341,7 +41155,7 @@ function entryKind2(path7) {
40341
41155
  }
40342
41156
  function visibleNames2(dir) {
40343
41157
  try {
40344
- return readdirSync17(dir).filter((name2) => !name2.startsWith(".")).sort((left, right) => left.localeCompare(right));
41158
+ return readdirSync18(dir).filter((name2) => !name2.startsWith(".")).sort((left, right) => left.localeCompare(right));
40345
41159
  } catch {
40346
41160
  return [];
40347
41161
  }
@@ -40350,7 +41164,7 @@ function readSelected(absolute, path7, entries) {
40350
41164
  let text2;
40351
41165
  let mtime;
40352
41166
  try {
40353
- text2 = readFileSync54(absolute, "utf8");
41167
+ text2 = readFileSync55(absolute, "utf8");
40354
41168
  mtime = statSync25(absolute).mtime.toISOString();
40355
41169
  } catch {
40356
41170
  return null;
@@ -40413,7 +41227,7 @@ var init_select3 = __esm(() => {
40413
41227
 
40414
41228
  // src/commands/skill/backfill.ts
40415
41229
  import { statSync as statSync26 } from "fs";
40416
- import { join as join61, resolve as resolve27 } from "path";
41230
+ import { join as join61, resolve as resolve28 } from "path";
40417
41231
  function readSwitch3(name2, value) {
40418
41232
  if (value === undefined || value === false || value === "false")
40419
41233
  return false;
@@ -40443,7 +41257,7 @@ Usage: ${BACKFILL_USAGE}`);
40443
41257
  throw new CommandExit(1, `\u274C Expected one directory, and read "${extra}" as a second
40444
41258
  Usage: ${BACKFILL_USAGE}`);
40445
41259
  }
40446
- return resolve27(cwd(), dir);
41260
+ return resolve28(cwd(), dir);
40447
41261
  }
40448
41262
  function readConsumerRoot(value, cwd) {
40449
41263
  if (value === undefined || value === false)
@@ -40452,7 +41266,7 @@ function readConsumerRoot(value, cwd) {
40452
41266
  throw new CommandExit(1, `\u274C --project needs a path, and the line gave it none
40453
41267
  Usage: ${BACKFILL_USAGE}`);
40454
41268
  }
40455
- return resolve27(cwd(), value);
41269
+ return resolve28(cwd(), value);
40456
41270
  }
40457
41271
  function isDirectory4(path7) {
40458
41272
  try {
@@ -40759,7 +41573,7 @@ var init_check3 = __esm(() => {
40759
41573
  });
40760
41574
 
40761
41575
  // src/demote/apply.ts
40762
- import { copyFileSync as copyFileSync5, existsSync as existsSync39, mkdirSync as mkdirSync18, mkdtempSync as mkdtempSync2, readdirSync as readdirSync18, readFileSync as readFileSync55, renameSync as renameSync5, rmSync as rmSync6, statSync as statSync27, unlinkSync as unlinkSync2, writeFileSync as writeFileSync20 } from "fs";
41576
+ import { copyFileSync as copyFileSync5, existsSync as existsSync41, mkdirSync as mkdirSync18, mkdtempSync as mkdtempSync2, readdirSync as readdirSync19, readFileSync as readFileSync56, renameSync as renameSync5, rmSync as rmSync6, statSync as statSync27, unlinkSync as unlinkSync2, writeFileSync as writeFileSync20 } from "fs";
40763
41577
  import { tmpdir as tmpdir2 } from "os";
40764
41578
  import { dirname as dirname21, join as join62 } from "path";
40765
41579
  function isLearnedPath(path7) {
@@ -40782,7 +41596,7 @@ function promotedPath(scope, path7) {
40782
41596
  function takenInstinctIds(dir) {
40783
41597
  let names;
40784
41598
  try {
40785
- names = readdirSync18(dir);
41599
+ names = readdirSync19(dir);
40786
41600
  } catch {
40787
41601
  return [];
40788
41602
  }
@@ -40790,7 +41604,7 @@ function takenInstinctIds(dir) {
40790
41604
  }
40791
41605
  function readText9(path7) {
40792
41606
  try {
40793
- return statSync27(path7).isFile() ? readFileSync55(path7, "utf8") : null;
41607
+ return statSync27(path7).isFile() ? readFileSync56(path7, "utf8") : null;
40794
41608
  } catch {
40795
41609
  return null;
40796
41610
  }
@@ -40818,7 +41632,7 @@ function planKept(scope, row3, file) {
40818
41632
  return inert3(row3.path, "procedure", "kept", "procedure-shaped: left where it is, for the backfill");
40819
41633
  }
40820
41634
  const to = promotedPath(scope, row3.path);
40821
- if (existsSync39(to) || existsSync39(dirname21(to))) {
41635
+ if (existsSync41(to) || existsSync41(dirname21(to))) {
40822
41636
  return inert3(row3.path, "procedure", "refused", `kept as a skill, but ${to} is already there`);
40823
41637
  }
40824
41638
  return {
@@ -40855,9 +41669,9 @@ function planDemoted(scope, row3, file, context) {
40855
41669
  }
40856
41670
  const destination = join62(scope.instinctsDir, `${id}${MARKDOWN_EXTENSION}`);
40857
41671
  const kept = demotedPath(scope, row3.path);
40858
- if (existsSync39(destination))
41672
+ if (existsSync41(destination))
40859
41673
  return inert3(row3.path, "observation", "refused", `${destination} is already there`);
40860
- if (existsSync39(kept))
41674
+ if (existsSync41(kept))
40861
41675
  return inert3(row3.path, "observation", "refused", `${kept} is already there`);
40862
41676
  context.taken.push(id);
40863
41677
  return {
@@ -40986,8 +41800,8 @@ var init_draft = __esm(() => {
40986
41800
  });
40987
41801
 
40988
41802
  // src/commands/skill/demote.ts
40989
- import { existsSync as existsSync40, mkdirSync as mkdirSync19, readFileSync as readFileSync56, statSync as statSync28, writeFileSync as writeFileSync21 } from "fs";
40990
- import { dirname as dirname22, resolve as resolve28 } from "path";
41803
+ import { existsSync as existsSync42, mkdirSync as mkdirSync19, readFileSync as readFileSync57, statSync as statSync28, writeFileSync as writeFileSync21 } from "fs";
41804
+ import { dirname as dirname22, resolve as resolve29 } from "path";
40991
41805
  function readApply(value) {
40992
41806
  if (value === undefined || value === false || value === "false")
40993
41807
  return false;
@@ -41006,7 +41820,7 @@ Usage: ${DEMOTE_USAGE}`);
41006
41820
  throw new CommandExit(1, `\u274C Expected one directory, and read "${extra}" as a second
41007
41821
  Usage: ${DEMOTE_USAGE}`);
41008
41822
  }
41009
- return resolve28(cwd(), dir);
41823
+ return resolve29(cwd(), dir);
41010
41824
  }
41011
41825
  function isDirectory5(path7) {
41012
41826
  try {
@@ -41038,9 +41852,9 @@ function verdictLine(report3) {
41038
41852
  return `${String(report3.rows.length)} file(s): ${parts.join(", ")}`;
41039
41853
  }
41040
41854
  function previousReport(path7, context) {
41041
- if (!existsSync40(path7))
41855
+ if (!existsSync42(path7))
41042
41856
  return null;
41043
- const parsed = parseDemotionReport(readFileSync56(path7, "utf8"));
41857
+ const parsed = parseDemotionReport(readFileSync57(path7, "utf8"));
41044
41858
  if (parsed.report !== null)
41045
41859
  return parsed.report;
41046
41860
  context.output.warn(`the report already at ${path7} does not parse, so no override in it is carried forward:` + ` ${parsed.issues.map((issue5) => `line ${String(issue5.line)} ${issue5.code}`).join(", ")}`);
@@ -41073,10 +41887,10 @@ function runWrite(context, scope) {
41073
41887
  context.output.info(line);
41074
41888
  }
41075
41889
  function reviewedReport(scope) {
41076
- if (!existsSync40(scope.reportPath)) {
41890
+ if (!existsSync42(scope.reportPath)) {
41077
41891
  throw new CommandExit(1, `\u274C No report at ${scope.reportPath}; run "rafa skill demote ${scope.dir}" first, then review it`);
41078
41892
  }
41079
- const parsed = parseDemotionReport(readFileSync56(scope.reportPath, "utf8"));
41893
+ const parsed = parseDemotionReport(readFileSync57(scope.reportPath, "utf8"));
41080
41894
  if (parsed.report === null) {
41081
41895
  const lines2 = parsed.issues.map((issue5) => ` line ${String(issue5.line)} ${issue5.code}: ${issue5.message}`);
41082
41896
  throw new CommandExit(1, [`\u274C ${scope.reportPath} does not parse:`, ...lines2].join(`
@@ -41434,9 +42248,9 @@ function boundedRunner(open, root, deadline) {
41434
42248
  }
41435
42249
  async function networkSection(what, deadline, work) {
41436
42250
  let timer;
41437
- const expired = new Promise((resolve29) => {
42251
+ const expired = new Promise((resolve30) => {
41438
42252
  timer = setTimeout(() => {
41439
- resolve29(unread2(`the ${what} was not read within the ${String(deadline.ms)}ms network deadline`));
42253
+ resolve30(unread2(`the ${what} was not read within the ${String(deadline.ms)}ms network deadline`));
41440
42254
  }, Math.max(0, deadline.left()));
41441
42255
  });
41442
42256
  try {
@@ -42414,7 +43228,7 @@ init_config_sections();
42414
43228
  init_sessions();
42415
43229
  init_scope();
42416
43230
  init_sections2();
42417
- import { mkdirSync as mkdirSync20, readFileSync as readFileSync57, renameSync as renameSync6, writeFileSync as writeFileSync22 } from "fs";
43231
+ import { mkdirSync as mkdirSync20, readFileSync as readFileSync58, renameSync as renameSync6, writeFileSync as writeFileSync22 } from "fs";
42418
43232
  import { dirname as dirname23, join as join63 } from "path";
42419
43233
  var SEEN_FILE = "status-seen.json";
42420
43234
  var SEEN_VERSION = 1;
@@ -42474,7 +43288,7 @@ function asSnapshot(value) {
42474
43288
  function readSeenFile(root) {
42475
43289
  let text2;
42476
43290
  try {
42477
- text2 = readFileSync57(seenFilePath(root), "utf8");
43291
+ text2 = readFileSync58(seenFilePath(root), "utf8");
42478
43292
  } catch {
42479
43293
  return null;
42480
43294
  }