@abloh/repository 1.0.0 → 1.0.1-staging.34767970367

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/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { RepoReader, PackageManifestReading, MatrixLeg, MatrixLegs, FailFastReading, SetupCandidateTrigger, SetupWorkflowCaller, SetupCandidateGate, NativeVocabulary, SetupFailFast, SetupCandidateExclusion, Refusal, YamlQuote, CoverageProviderChoice, MatrixValue, WorkflowNodePin, SetupScriptStep, ValueOrigin, CommandScriptHop, AngularTestTarget, PreparedTestCommand, InitFailureClass, InitQuestionKey, EnvironmentConfig, TargetSelectionKind, EvidenceOutputId, resolvePreparedTestCommand, MeasurementPlan, PackageExclusion } from '@abloh/core';
1
+ import { RepoReader, PackageManifestReading, ProposedFile, MatrixLeg, MatrixLegs, FailFastReading, SetupCandidateTrigger, SetupWorkflowCaller, SetupCandidateGate, NativeVocabulary, SetupFailFast, SetupCandidateExclusion, Refusal, YamlQuote, CoverageProviderChoice, MatrixValue, WorkflowNodePin, SetupScriptStep, ValueOrigin, CommandScriptHop, AngularTestTarget, PreparedTestCommand, InitFailureClass, InitQuestionKey, EnvironmentConfig, TargetSelectionKind, EvidenceOutputId, resolvePreparedTestCommand, MeasurementPlan, PackageExclusion } from '@abloh/core';
2
2
  export { SETUP_STEP_ACTION, SETUP_STEP_IF, SETUP_STEP_JOB_STATUS, SETUP_STEP_NAME, setupStepIf } from '@abloh/core';
3
3
  import { JasmineSuite, ProofRunner, StrykerMutationRunner } from '@abloh/measure';
4
4
 
@@ -56,6 +56,17 @@ interface RepoAccess {
56
56
  }
57
57
  /** The access `abloh init` and every existing caller get: a checkout on this machine. */
58
58
  declare function diskRepoAccess(repoDir: string): RepoAccess;
59
+ /**
60
+ * THE SAME ACCESS, READING {@link ProposedFile} BYTES FOR THE PATHS SOMEBODY HAS AGREED TO.
61
+ *
62
+ * WHAT IT IS FOR (the audit's class 5, 2026-09-12). `abloh init` has to draft as if an answered edit
63
+ * to a file of the maintainer's were already there, and it used to do that by writing the file and
64
+ * reading it back - before the confirmation that says abloh has written nothing yet. The root, the
65
+ * spellings of it and the realpath rule are untouched: only what the reader answers moves.
66
+ *
67
+ * An empty list returns the access itself, so the ordinary road pays nothing.
68
+ */
69
+ declare function overlayRepoAccess(base: RepoAccess, files: readonly ProposedFile[]): RepoAccess;
59
70
  /**
60
71
  * The repository-relative path an absolute one names, or null when it names nothing in here.
61
72
  *
@@ -975,6 +986,20 @@ interface CiRecipe {
975
986
  * sets environment values Abloh has no key for, a build scoped to one workspace package.
976
987
  */
977
988
  notices: readonly string[];
989
+ /**
990
+ * THE CHOSEN JOB'S OWN STEPS THAT ABLOH READ AND DID NOT COPY INTO `.abloh/setup.sh`.
991
+ *
992
+ * THE RULING (the captain, 2026-09-12, review finding F4): init copies only the steps the tests
993
+ * need, a lint, format, docs or publish step is not one of them, and the maintainer is told which
994
+ * ones were left out. `honojs/hono` is the repository that earned it - its job runs `format`,
995
+ * `lint` and `editorconfig-checker` before its suite, and the checker downloads a release binary
996
+ * and cannot, so a repository that was never measured stopped on a step about trailing newlines.
997
+ *
998
+ * NOT {@link refusal} AND NOT A REFUSAL'S LIST. Those say abloh could not READ a step; these say
999
+ * abloh read it and the suite does not read what it produces. Only the first is a gap in abloh's
1000
+ * reading, and folding the two would tell a maintainer to go and copy in a linter.
1001
+ */
1002
+ stepsNotCopied: readonly CiDroppedStep[];
978
1003
  }
979
1004
  type AptReading = {
980
1005
  ok: true;
@@ -1029,6 +1054,21 @@ interface NodeEnvironment {
1029
1054
  * cell does Abloh's step actually run on".
1030
1055
  */
1031
1056
  declare function readCiNodeDeclaration(raw: string | null, matrix: unknown, reader: RepoReader, leg?: MatrixLeg | null | undefined, env?: NodeEnvironment): CiNodeVersion | null;
1057
+ /** One step of a candidate job abloh read and declined to translate, with its own sentence. */
1058
+ /**
1059
+ * ONE STEP ABLOH READ AND DID NOT COPY: where it sits, what it runs, and why it was left out.
1060
+ *
1061
+ * THE COMMAND IS THE MAINTAINER'S OWN TEXT, so it is local-only wherever it is shown - the same
1062
+ * rule `setup-ci-steps-unread` already carries its step bodies under.
1063
+ */
1064
+ interface CiDroppedStep {
1065
+ /** 1-based, as a maintainer counts the `steps:` of their own job. Null for Travis and CircleCI. */
1066
+ step: number | null;
1067
+ /** The step's own command line. */
1068
+ command: string;
1069
+ /** Why the suite does not need it, in the reading's own words. */
1070
+ why: string;
1071
+ }
1032
1072
  /**
1033
1073
  * DO TWO JOB REFERENCES NAME THE SAME JOB, whichever spelling each of them uses?
1034
1074
  *
@@ -2219,8 +2259,27 @@ interface DraftService {
2219
2259
  *
2220
2260
  * A constant because a re-run has to RECOGNIZE it: the block is replaced from its own `environment:`
2221
2261
  * line down, and a header left stranded above the replacement appears twice in the customer's file.
2262
+ *
2263
+ * IT NAMES CI RATHER THAN A CONTAINER (the audit of 2026-09-12, 10.4). "the proof container" is a
2264
+ * thing one road builds: a run on the maintainer's own machine measures a copy of their own tree
2265
+ * with their own installed dependencies, and reads none of the keys under this header. The block is
2266
+ * still written on both roads, because it is what their CI needs the day they add abloh's check
2267
+ * there, and the header now says so instead of describing work that is not happening.
2222
2268
  */
2223
- declare const ENVIRONMENT_BLOCK_HEADER = "# Rebuild recipe for the proof container. Comments name the source file.";
2269
+ declare const ENVIRONMENT_BLOCK_HEADER = "# How CI rebuilds this project for abloh's check. Comments name the source file.";
2270
+ /**
2271
+ * HEADERS THIS COMMAND USED TO WRITE, so a re-run still recognizes a file it wrote before.
2272
+ *
2273
+ * WHY IT EXISTS. The recognition above is an exact string match. Change the constant with no list
2274
+ * beside it and every `abloh.yml` already carrying the old line gets the new header written above
2275
+ * the old one - the exact duplication that constant's own header warns about, arriving through the
2276
+ * one edit it did not anticipate.
2277
+ *
2278
+ * IT ONLY GROWS. A line here is a line some repository has committed, and nothing rewrites it.
2279
+ */
2280
+ declare const RETIRED_ENVIRONMENT_BLOCK_HEADERS: readonly string[];
2281
+ /** Is this line an environment-block header this command has ever written? */
2282
+ declare function isEnvironmentBlockHeader(line: string): boolean;
2224
2283
  /**
2225
2284
  * WHAT ABLOH READ ABOUT THE JOB ITS STEP RIDES, as the contract will state it.
2226
2285
  *
@@ -4427,6 +4486,29 @@ declare function placeSetupStep(input: {
4427
4486
  before: string;
4428
4487
  } | null;
4429
4488
  }): SetupStepPlacement | Refusal;
4489
+ /**
4490
+ * Is abloh's step already the last thing this job does?
4491
+ *
4492
+ * MATCHED ON THE ACTION REPOSITORY AND NOT ON THE PIN. A customer who is already running abloh at
4493
+ * an older commit has an abloh step, and appending a second one would run the measurement twice and
4494
+ * put two check runs on every pull request. The pin is ours to bump and theirs to keep.
4495
+ */
4496
+ /**
4497
+ * DOES THIS WORKFLOW ALREADY RUN ABLOH, in any job of it?
4498
+ *
4499
+ * WHO ASKS (the captain's ruling of 2026-09-12). Re-running `abloh init` adds a lane, so the command
4500
+ * has to be able to tell a repository that is already set up for pull requests from one that is not
4501
+ * - and the step in one of the maintainer's own jobs is the fact that says so.
4502
+ *
4503
+ * IT IS THE SAME MATCHER THE WRITER USES, over every job rather than over one. A second reading of
4504
+ * "is abloh's step here" is how one of them comes to answer differently, and the writer's answer is
4505
+ * what decides whether a second step is appended.
4506
+ *
4507
+ * AN UNREADABLE FILE ANSWERS FALSE. What that costs is an offer to set up a lane that is already
4508
+ * there, which a maintainer can decline. Answering true would hide the offer from somebody who
4509
+ * needs it.
4510
+ */
4511
+ declare function workflowRunsAbloh(text: string): boolean;
4430
4512
 
4431
4513
  /**
4432
4514
  * WHICH OF THE MAINTAINER'S SUITE STEPS ABLOH'S STEP GOES BEHIND, ON THE LEG IT PINNED.
@@ -4628,4 +4710,4 @@ declare function selectAutoTarget(input: {
4628
4710
  */
4629
4711
  declare function committedYarnVersion(directory: string, lockfileName: string, ctx?: RepoAccess): string | null;
4630
4712
 
4631
- export { ANSWERABLE_SETUP_KEYS, ANSWERED_EVIDENCE, type AggregatorInvocation, type AggregatorTool, type AttestationJob, type AutoTargetSelection, type BrowserLane, type BrowserLaneEvidence, type BrowserLaneKind, CI_RECIPE_REFUSALS, type CiBorrowJob, type CiBrowserLane, type CiEnvironmentValue, type CiInstallStep, type CiJobStep, type CiJobTestStep, type CiManagerVersion, type CiNodeVersion, type CiRecipe, type CiRequiredVariable, type CiService, type CiSetupCommand, type CiSystemPackage, type CiTestCommand, type CiUnreadableJobCall, type CiUnsupportedService, type ConfigInput, type ConfigSystemPackage, type CoverageChildSuite, DENO_CONFIG_FILES, DENO_LOCKFILE, type DeclaredBinding, type DeclaredCommandContext, type DenoProject, type DraftService, ENVIRONMENT_BLOCK_HEADER, InitRefusal, type LockfileChoice, type ManifestClassification, type NodeChoice, type NodeDeclaration, type NodeDeclarationProblem, type NodeDeclarationRejection, type NodeEnvironment, type NodeRelease, type NodeRequirement, type NodeSpecReading, type NxProject, type NxTestTarget, type PackageManager, type PackageManagerContext, type PackageManagerResolution, type PlanDeclaration, type PolicyDeclaration, RUSH_CONFIG_DIRECTORY, RUSH_CONFIG_FILE, RUSH_INSTALL_COMMAND, RUSH_INSTALL_SCRIPT, RUSH_LOCKFILE_NAME, RUSH_REPO_STATE_NAME, RUSH_SUBSPACE_DIRECTORY, RUSH_TEST_SCRIPTS, type ReadTextAt, type RebuildDraftValue, type RepoAccess, type ResolveMeasurementPlanInput, type Runner, type RuntimeNeed, type RushProject, type RushTestTarget, SETUP_QUESTION_DESCRIPTORS, STEP_WHAT, type SetupAnswerChannel, type SetupAnswerShape, type SetupDraftValues, type SetupFailFastDecision, type SetupJobDecision, type SetupJobQuestion, type SetupMatrixLeg, type SetupPin, type SetupPublishPlacement, type SetupQuestionDescriptor, type SetupStepAnchor, type SetupStepPlacement, type SetupStepPrecondition, type SubmoduleRoot, type TargetDetection, TargetDetectionError, TargetNotFoundError, type TargetNotFoundReason, type TargetResolution, type TargetResolutionInput, type WorkflowEnvironmentEntry, type WorkflowEnvironmentLevel, type WorkflowEnvironmentScope, type WorkflowExpressionFacts, type WorkflowExpressionReading, type WorkspaceEnumeration, type WorkspaceRootSuite, __testing, accessExists, accessKind, accessManifest, accessSubdirectories, accessText, admitSubmodules, aggregatorInvocation, aggregatorTaskPhrase, aliasHopFamily, answeredTargetDirectory, applyAnswersToContract, applyDeclaredRunner, argvInvokesNodeTest, assertNoGitlinkChanges, bindDeclaredCommand, bindPreparedTestCommand, chooseNodeVersion, ciInstallSteps, ciInstallersFor, ciManagerVersions, ciRefusalIsActionable, classicPerTestRefusal, classicPerTestRunner, classifyNxTestTarget, classifyPackageDirectory, classifyPackageManifest, classifyRushTestScript, classifyWithAliasHop, commentLines, committedYarnVersion, contractDescriptors, decideSetupJob, declarableSetupJobs, declaredCommandContext, declaredRunner, declaredRunnerInvocation, declaredSetupJob, declaredSetupPin, denoLockfilePath, denoTestInvocation, deriveCiRecipe, detectJasmineSuite, detectPackageManager, detectPackageManagerContext, detectTarget, detectVitestDir, diskRepoAccess, diskRepoReader, doorSetupJobs, emptyCiRecipe, engineRunner, enumerateNxProjects, enumerateRushProjects, enumerateWorkspacePackages, enumerateWorkspacePackagesOnDisk, initFailureClassOf, initRefusal, initStepRole, invokesNodeTest, isBrowserInstallCommand, isInstallStep, jasmineSuiteFromScript, layerEnvironment, materializeSubmodule, nodeDeclarationProblemReason, nodeSpecVersions, placeSetupStep, policyDeclaration, preRunnerStageCommands, readAnswerValue, readBrowserLane, readDenoProject, readNodeSpec, readWorkflowExpressions, referencesExpression, referencesSecret, refusalRemedyOf, renderConfig, renderEnvironmentBlock, renderWorkflow, repoPath, resolveMeasurementPlan, resolveSetupPin, resolveTarget, resolveTargetDetection, rootAggregatorTasks, runnerInvocation, runnerStageCommand, runnerSuiteInvocations, rushIdentityFiles, rushLockfiles, rushNodeVersionRange, sameJobReference, scriptRunsDenoTest, selectAutoTarget, setupAnswerProblem, setupJobCandidates, setupJobReference, setupScriptSteps, setupStepAnchor, setupStepOrigin, splitAnswerList, suiteStages, systemPackagesStep, testCommandReceipt, testStepLabel, testableWorkspacePackages, unreadableConditionLine, vitestDirIn, withoutPackageRunnerPrefix, workDirPrefix };
4713
+ export { ANSWERABLE_SETUP_KEYS, ANSWERED_EVIDENCE, type AggregatorInvocation, type AggregatorTool, type AttestationJob, type AutoTargetSelection, type BrowserLane, type BrowserLaneEvidence, type BrowserLaneKind, CI_RECIPE_REFUSALS, type CiBorrowJob, type CiBrowserLane, type CiDroppedStep, type CiEnvironmentValue, type CiInstallStep, type CiJobStep, type CiJobTestStep, type CiManagerVersion, type CiNodeVersion, type CiRecipe, type CiRequiredVariable, type CiService, type CiSetupCommand, type CiSystemPackage, type CiTestCommand, type CiUnreadableJobCall, type CiUnsupportedService, type ConfigInput, type ConfigSystemPackage, type CoverageChildSuite, DENO_CONFIG_FILES, DENO_LOCKFILE, type DeclaredBinding, type DeclaredCommandContext, type DenoProject, type DraftService, ENVIRONMENT_BLOCK_HEADER, InitRefusal, type LockfileChoice, type ManifestClassification, type NodeChoice, type NodeDeclaration, type NodeDeclarationProblem, type NodeDeclarationRejection, type NodeEnvironment, type NodeRelease, type NodeRequirement, type NodeSpecReading, type NxProject, type NxTestTarget, type PackageManager, type PackageManagerContext, type PackageManagerResolution, type PlanDeclaration, type PolicyDeclaration, RETIRED_ENVIRONMENT_BLOCK_HEADERS, RUSH_CONFIG_DIRECTORY, RUSH_CONFIG_FILE, RUSH_INSTALL_COMMAND, RUSH_INSTALL_SCRIPT, RUSH_LOCKFILE_NAME, RUSH_REPO_STATE_NAME, RUSH_SUBSPACE_DIRECTORY, RUSH_TEST_SCRIPTS, type ReadTextAt, type RebuildDraftValue, type RepoAccess, type ResolveMeasurementPlanInput, type Runner, type RuntimeNeed, type RushProject, type RushTestTarget, SETUP_QUESTION_DESCRIPTORS, STEP_WHAT, type SetupAnswerChannel, type SetupAnswerShape, type SetupDraftValues, type SetupFailFastDecision, type SetupJobDecision, type SetupJobQuestion, type SetupMatrixLeg, type SetupPin, type SetupPublishPlacement, type SetupQuestionDescriptor, type SetupStepAnchor, type SetupStepPlacement, type SetupStepPrecondition, type SubmoduleRoot, type TargetDetection, TargetDetectionError, TargetNotFoundError, type TargetNotFoundReason, type TargetResolution, type TargetResolutionInput, type WorkflowEnvironmentEntry, type WorkflowEnvironmentLevel, type WorkflowEnvironmentScope, type WorkflowExpressionFacts, type WorkflowExpressionReading, type WorkspaceEnumeration, type WorkspaceRootSuite, __testing, accessExists, accessKind, accessManifest, accessSubdirectories, accessText, admitSubmodules, aggregatorInvocation, aggregatorTaskPhrase, aliasHopFamily, answeredTargetDirectory, applyAnswersToContract, applyDeclaredRunner, argvInvokesNodeTest, assertNoGitlinkChanges, bindDeclaredCommand, bindPreparedTestCommand, chooseNodeVersion, ciInstallSteps, ciInstallersFor, ciManagerVersions, ciRefusalIsActionable, classicPerTestRefusal, classicPerTestRunner, classifyNxTestTarget, classifyPackageDirectory, classifyPackageManifest, classifyRushTestScript, classifyWithAliasHop, commentLines, committedYarnVersion, contractDescriptors, decideSetupJob, declarableSetupJobs, declaredCommandContext, declaredRunner, declaredRunnerInvocation, declaredSetupJob, declaredSetupPin, denoLockfilePath, denoTestInvocation, deriveCiRecipe, detectJasmineSuite, detectPackageManager, detectPackageManagerContext, detectTarget, detectVitestDir, diskRepoAccess, diskRepoReader, doorSetupJobs, emptyCiRecipe, engineRunner, enumerateNxProjects, enumerateRushProjects, enumerateWorkspacePackages, enumerateWorkspacePackagesOnDisk, initFailureClassOf, initRefusal, initStepRole, invokesNodeTest, isBrowserInstallCommand, isEnvironmentBlockHeader, isInstallStep, jasmineSuiteFromScript, layerEnvironment, materializeSubmodule, nodeDeclarationProblemReason, nodeSpecVersions, overlayRepoAccess, placeSetupStep, policyDeclaration, preRunnerStageCommands, readAnswerValue, readBrowserLane, readDenoProject, readNodeSpec, readWorkflowExpressions, referencesExpression, referencesSecret, refusalRemedyOf, renderConfig, renderEnvironmentBlock, renderWorkflow, repoPath, resolveMeasurementPlan, resolveSetupPin, resolveTarget, resolveTargetDetection, rootAggregatorTasks, runnerInvocation, runnerStageCommand, runnerSuiteInvocations, rushIdentityFiles, rushLockfiles, rushNodeVersionRange, sameJobReference, scriptRunsDenoTest, selectAutoTarget, setupAnswerProblem, setupJobCandidates, setupJobReference, setupScriptSteps, setupStepAnchor, setupStepOrigin, splitAnswerList, suiteStages, systemPackagesStep, testCommandReceipt, testStepLabel, testableWorkspacePackages, unreadableConditionLine, vitestDirIn, withoutPackageRunnerPrefix, workDirPrefix, workflowRunsAbloh };
package/dist/index.js CHANGED
@@ -3,6 +3,7 @@ import { realpathSync as realpathSync2 } from "fs";
3
3
  import { relative, resolve as resolve2 } from "path";
4
4
  import {
5
5
  normalizeRepoPath as normalizeRepoPath2,
6
+ overlayRepoReader,
6
7
  readPackageManifest
7
8
  } from "@abloh/core";
8
9
 
@@ -116,6 +117,10 @@ function diskRepoAccess(repoDir) {
116
117
  }
117
118
  };
118
119
  }
120
+ function overlayRepoAccess(base, files) {
121
+ const reader = overlayRepoReader(base.reader, files);
122
+ return reader === base.reader ? base : { ...base, reader };
123
+ }
119
124
  function repoPath(ctx, absolute) {
120
125
  const target = resolve2(absolute);
121
126
  for (const base of ctx.roots) {
@@ -320,6 +325,7 @@ import {
320
325
  manifestScripts,
321
326
  normalizeRepoPath as normalizeRepoPath3,
322
327
  readPackageManifest as readPackageManifest2,
328
+ runIsDroppable,
323
329
  scriptRuns
324
330
  } from "@abloh/core";
325
331
  import {
@@ -1037,7 +1043,7 @@ function readStep(step, manifest, reader) {
1037
1043
  const script = workspaceFirst[2];
1038
1044
  if (TEST_NAMES.test(script)) return { kind: "test", command };
1039
1045
  if (BUILD_NAMES.test(script)) return { kind: "build", command, env };
1040
- return { kind: "other" };
1046
+ return { kind: "other", command, why: "abloh could not read what it does" };
1041
1047
  }
1042
1048
  const scoped = /--workspace[= ]([^\s]+)|--filter[= ]([^\s]+)/u.test(tail);
1043
1049
  if (manifest.scripts[name] !== void 0 || scoped) {
@@ -1050,7 +1056,7 @@ function readStep(step, manifest, reader) {
1050
1056
  if (manifest.scripts[name] !== void 0 && scriptRunsBuild(manifest, name)) {
1051
1057
  return { kind: "build", command, env };
1052
1058
  }
1053
- return { kind: "other" };
1059
+ return { kind: "other", command, why: scriptNotABuildBecause(manifest, name) };
1054
1060
  }
1055
1061
  if (TEST_BINARIES.has(name)) return { kind: "test", command };
1056
1062
  if (BUILD_BINARIES.has(name)) {
@@ -1170,7 +1176,20 @@ function scriptRunsBuild(manifest, name) {
1170
1176
  lifecycle: false
1171
1177
  });
1172
1178
  if (runs.length === 0) return false;
1173
- return runs.every((run) => run.kind === "preparation" && (run.role === "build" || run.role === "tool"));
1179
+ return runs.every((run) => run.kind === "preparation" && run.role === "build");
1180
+ }
1181
+ function scriptNotABuildBecause(manifest, name) {
1182
+ const runs = scriptRuns({
1183
+ scripts: manifest.scripts,
1184
+ dependencies: manifest.dependencies,
1185
+ script: name,
1186
+ lifecycle: false
1187
+ });
1188
+ if (runs.length > 0 && runs.every((run) => runIsDroppable(run) && run.kind !== "test")) {
1189
+ return "it checks the tree rather than writing into it";
1190
+ }
1191
+ const unknown = runs.find((run) => run.kind === "preparation" && run.role === "tool");
1192
+ return unknown === void 0 ? "abloh could not read what it does" : `abloh has no reading of ${unknown.name}, so it cannot say the suite needs it`;
1174
1193
  }
1175
1194
  function clip(command) {
1176
1195
  return command.length > 70 ? `${command.slice(0, 70)}\u2026` : command;
@@ -1334,6 +1353,7 @@ function emptyCiRecipe(refusal) {
1334
1353
  browserLane: null,
1335
1354
  unreadableJobCalls: [],
1336
1355
  refusal,
1356
+ stepsNotCopied: [],
1337
1357
  notices: []
1338
1358
  };
1339
1359
  }
@@ -1359,6 +1379,7 @@ function deriveCiRecipe(input) {
1359
1379
  browserLane: null,
1360
1380
  unreadableJobCalls: [],
1361
1381
  refusal: `your CI configuration could not be read (${error instanceof Error ? error.message : "unknown error"})`,
1382
+ stepsNotCopied: [],
1362
1383
  notices: []
1363
1384
  };
1364
1385
  }
@@ -1385,6 +1406,7 @@ function derive(input) {
1385
1406
  runnerImage: null,
1386
1407
  browserLane: null,
1387
1408
  unreadableJobCalls: calls,
1409
+ stepsNotCopied: [],
1388
1410
  refusal,
1389
1411
  notices: [...bounds, ...notices]
1390
1412
  });
@@ -1598,20 +1620,21 @@ function assemble(input) {
1598
1620
  for (const refused of chosen.refusals) {
1599
1621
  if (!unread.some((seen2) => seen2.step === refused.step && seen2.why === refused.why)) unread.push(refused);
1600
1622
  }
1601
- const unreadMessage = messageFromFacts({
1602
- kind: "setup-ci-steps",
1603
- unread,
1623
+ const stepsRead = {
1604
1624
  workflow: chosen.workflowPath,
1605
1625
  job: chosen.jobId,
1606
1626
  /* THE MAINTAINER'S OWN FILE SAID IT, and `at` is the receipt spelling every other notice in
1607
1627
  this function uses, so a reader meets one name for the job on every line. */
1608
1628
  at: `${chosen.file}::${chosen.jobId}`
1609
- });
1610
- if (unreadMessage.kind === "message") {
1611
- const refusal = unreadMessage.refusal;
1612
- const rendered = renderRefusal(refusal, "terminal");
1629
+ };
1630
+ const sayStepsRead = (facts2) => {
1631
+ const outcome = messageFromFacts({ kind: "setup-ci-steps", ...stepsRead, ...facts2 });
1632
+ if (outcome.kind !== "message") return;
1633
+ const rendered = renderRefusal(outcome.refusal, "terminal");
1613
1634
  if (rendered !== null) notices.push(refusalLines(rendered).join("\n"));
1614
- }
1635
+ };
1636
+ sayStepsRead({ unread, notCopied: [] });
1637
+ sayStepsRead({ unread: [], notCopied: [...chosen.notCopied] });
1615
1638
  const drafted = [];
1616
1639
  const seen = /* @__PURE__ */ new Set();
1617
1640
  const push = (step, source) => {
@@ -1838,6 +1861,11 @@ function assemble(input) {
1838
1861
  runnerImage,
1839
1862
  browserLane,
1840
1863
  unreadableJobCalls: input.unreadableJobCalls,
1864
+ /* THE STEPS THE CHOSEN JOB RUNS THAT THE RECIPE LEFT OUT. See {@link CiRecipe.stepsNotCopied}:
1865
+ this is the chosen candidate's own list and never a pooling of every job's, because the
1866
+ recipe is derived from one job and a sentence about another job's steps would name edits to a
1867
+ file abloh is not reading. */
1868
+ stepsNotCopied: chosen.notCopied,
1841
1869
  refusal: null,
1842
1870
  notices
1843
1871
  };
@@ -2052,6 +2080,7 @@ function blankCandidate(file, jobId, label) {
2052
2080
  testSteps: [],
2053
2081
  installs: false,
2054
2082
  refusals: [],
2083
+ notCopied: [],
2055
2084
  refusedTestSteps: [],
2056
2085
  /* A `.travis.yml` or a CircleCI job declares no `env:` block and no `services:` this module has
2057
2086
  a preset for, so the three keys those produce are empty here by construction. */
@@ -2489,6 +2518,7 @@ function readJob(reader, manifest, file, jobId, job, workflowEnv, bounds = []) {
2489
2518
  testSteps: [],
2490
2519
  installs: false,
2491
2520
  refusals: [],
2521
+ notCopied: [],
2492
2522
  refusedTestSteps: [],
2493
2523
  node: null,
2494
2524
  nodeEnv: NO_NODE_ENVIRONMENT,
@@ -2595,6 +2625,15 @@ function readJob(reader, manifest, file, jobId, job, workflowEnv, bounds = []) {
2595
2625
  candidate.refusedTestSteps.push(classified.why);
2596
2626
  }
2597
2627
  break;
2628
+ case "other":
2629
+ if (!candidate.notCopied.some((seen) => seen.command === classified.command)) {
2630
+ candidate.notCopied.push({
2631
+ step: stepNumber(step),
2632
+ command: classified.command,
2633
+ why: classified.why
2634
+ });
2635
+ }
2636
+ break;
2598
2637
  default:
2599
2638
  break;
2600
2639
  }
@@ -2790,7 +2829,7 @@ function renderEnvironmentBlock(input) {
2790
2829
  ...input.coverageProvider === void 0 ? [] : [{
2791
2830
  key: "coverageProvider",
2792
2831
  value: input.coverageProvider,
2793
- receipt: input.coverageProvider === "install" ? "you agreed abloh may stage @vitest/coverage-v8 in the job that measures, into its own cache outside this repository" : "you asked abloh not to install @vitest/coverage-v8, so the changed-line check reports itself as declined"
2832
+ receipt: input.coverageProvider === "install" ? "you agreed abloh may stage @vitest/coverage-v8 where a run measures, into its own cache outside this repository" : "you asked abloh not to install @vitest/coverage-v8, so the changed-line check reports itself as declined"
2794
2833
  }],
2795
2834
  ...input.sealedTestCommand !== null || input.sealedNote !== null ? [{
2796
2835
  key: "sealedTestCommand",
@@ -2838,7 +2877,7 @@ function renderEnvironmentBlock(input) {
2838
2877
  }
2839
2878
  }
2840
2879
  if (input.identityFiles.length > 0) {
2841
- lines.push(" identityFiles: # a change here triggers reinstall instead of container reuse");
2880
+ lines.push(" identityFiles: # a change here makes CI reinstall instead of reusing its image");
2842
2881
  for (const path of input.identityFiles) lines.push(` - ${yamlScalar(path, quote)}`);
2843
2882
  }
2844
2883
  if (input.requiredVariables.length > 0) {
@@ -2952,7 +2991,13 @@ function receiptLines(entries, indent, quote) {
2952
2991
  }
2953
2992
  return lines;
2954
2993
  }
2955
- var ENVIRONMENT_BLOCK_HEADER = "# Rebuild recipe for the proof container. Comments name the source file.";
2994
+ var ENVIRONMENT_BLOCK_HEADER = "# How CI rebuilds this project for abloh's check. Comments name the source file.";
2995
+ var RETIRED_ENVIRONMENT_BLOCK_HEADERS = [
2996
+ "# Rebuild recipe for the proof container. Comments name the source file."
2997
+ ];
2998
+ function isEnvironmentBlockHeader(line) {
2999
+ return line === ENVIRONMENT_BLOCK_HEADER || RETIRED_ENVIRONMENT_BLOCK_HEADERS.includes(line);
3000
+ }
2956
3001
  var STEP_WHAT = {
2957
3002
  systemPackages: "OS packages your suite needs, pinned",
2958
3003
  corepack: "put the package manager on PATH, for your own lifecycle scripts",
@@ -2977,7 +3022,9 @@ function isInstallStep(what) {
2977
3022
  return what === STEP_WHAT.install || what === STEP_WHAT.installUnlocked;
2978
3023
  }
2979
3024
  function setupStepOrigin(step) {
2980
- return initStepRole(step.what) === null ? CUSTOMER_TYPED : "abloh-derived";
3025
+ const role = initStepRole(step.what);
3026
+ if (role === null) return CUSTOMER_TYPED;
3027
+ return role === "setupCommands" ? "abloh-composed" : "abloh-derived";
2981
3028
  }
2982
3029
  function initStepRole(what) {
2983
3030
  if (what === STEP_WHAT.systemPackages) return "systemPackages";
@@ -7278,6 +7325,20 @@ function failFastInsertion(lines, job) {
7278
7325
  }
7279
7326
  return null;
7280
7327
  }
7328
+ function workflowRunsAbloh(text) {
7329
+ let document;
7330
+ try {
7331
+ document = parse3(text);
7332
+ } catch {
7333
+ return false;
7334
+ }
7335
+ const jobs = document?.jobs;
7336
+ if (typeof jobs !== "object" || jobs === null) return false;
7337
+ return Object.values(jobs).some((job) => {
7338
+ const steps = job?.steps;
7339
+ return Array.isArray(steps) && alreadyEndsInAblohStep(steps);
7340
+ });
7341
+ }
7281
7342
  function alreadyEndsInAblohStep(steps) {
7282
7343
  const repository = SETUP_STEP_ACTION.split("@")[0] ?? SETUP_STEP_ACTION;
7283
7344
  return steps.some(
@@ -7593,6 +7654,7 @@ export {
7593
7654
  DENO_LOCKFILE,
7594
7655
  ENVIRONMENT_BLOCK_HEADER,
7595
7656
  InitRefusal,
7657
+ RETIRED_ENVIRONMENT_BLOCK_HEADERS,
7596
7658
  RUSH_CONFIG_DIRECTORY,
7597
7659
  RUSH_CONFIG_FILE,
7598
7660
  RUSH_INSTALL_COMMAND,
@@ -7670,12 +7732,14 @@ export {
7670
7732
  initStepRole,
7671
7733
  invokesNodeTest,
7672
7734
  isBrowserInstallCommand,
7735
+ isEnvironmentBlockHeader,
7673
7736
  isInstallStep,
7674
7737
  jasmineSuiteFromScript,
7675
7738
  layerEnvironment,
7676
7739
  materializeSubmodule,
7677
7740
  nodeDeclarationProblemReason,
7678
7741
  nodeSpecVersions,
7742
+ overlayRepoAccess,
7679
7743
  placeSetupStep,
7680
7744
  policyDeclaration,
7681
7745
  preRunnerStageCommands,
@@ -7721,5 +7785,6 @@ export {
7721
7785
  unreadableConditionLine,
7722
7786
  vitestDirIn,
7723
7787
  withoutPackageRunnerPrefix,
7724
- workDirPrefix
7788
+ workDirPrefix,
7789
+ workflowRunsAbloh
7725
7790
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@abloh/repository",
3
- "version": "1.0.0",
3
+ "version": "1.0.1-staging.34767970367",
4
4
  "description": "Reads a repository and decides what abloh may measure in it: the CI recipe, the target, the package manager, the workspace packages, the job abloh's step rides and the measurement plan. Shared by the CLI, which reads a checkout, and by the control plane, which reads the same repository over GitHub's contents API. \"name\" will publish under the @abloh scope.",
5
5
  "type": "module",
6
6
  "license": "SEE LICENSE IN LICENSE",
@@ -23,8 +23,8 @@
23
23
  "dependencies": {
24
24
  "semver": "7.8.5",
25
25
  "yaml": "^2.6.1",
26
- "@abloh/core": "1.0.0",
27
- "@abloh/measure": "1.0.0"
26
+ "@abloh/measure": "1.0.1-staging.34767970367",
27
+ "@abloh/core": "1.0.1-staging.34767970367"
28
28
  },
29
29
  "devDependencies": {
30
30
  "@types/semver": "7.7.1",