bareloop 0.25.0 → 0.26.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.
@@ -431,14 +431,29 @@ export const MAX_STAGES: 12;
431
431
  */
432
432
  export const DENIED_COMMANDS: readonly string[];
433
433
  /**
434
- * THE FROZEN TYPES GENRE TEMPLATE — prereg addendum 3's six policy points,
435
- * VERBATIM. The suite reads the prereg and compares BYTES, so a paraphrase here
436
- * fails the build rather than quietly changing what every future close is
437
- * authored against. It deliberately carries no repo-specific command, path, env
438
- * value or regex: round 2 measured that stating the LAW still leaves the model
439
- * to find the VALUE, and that is the axis the build must keep live.
434
+ * THE TYPES GENRE TEMPLATE — six policy points, VERBATIM against
435
+ * `docs/product/ITEM33-BUILD.md`'s own frozen block (PRD item 33 M3 piece 4,
436
+ * D6 = A ruling, 2026-09-14). This SUPERSEDES the original prereg-frozen
437
+ * version (`docs/logs/2026-08-08-close-authoring-gate2-poc-prereg.md`,
438
+ * addendum 3, the closed record — never edited): that version stated the
439
+ * WORK-stage skeleton (typecheck, typecheck-outside, tests-kept,
440
+ * suite-green) and the checker's STRICT form as UNCONDITIONAL law, which
441
+ * conflicted with ruling 6's "a genre never adds a check the goal did not
442
+ * ask for" — run mtv8jihy's unasked `tsc --strict` stage is the case this
443
+ * cures. Composing those stages/strictness only for a CONFIRMED check
444
+ * (never a code matcher over prose, same reasoning as the no-shell law) is
445
+ * enforced here, in the prompt; the GUARDS (changed-from-seed,
446
+ * no-suppressions) are enforced separately and structurally
447
+ * (`classGuards` + `validateDeclaration`'s `guards-absent` red), so making
448
+ * the WORK stages conditional here changes nothing about their own
449
+ * always-on status. The suite reads the SIGNED block and compares BYTES, so
450
+ * a paraphrase here fails the build rather than quietly changing what every
451
+ * future close is authored against. It deliberately carries no
452
+ * repo-specific command, path, env value or regex: round 2 measured that
453
+ * stating the LAW still leaves the model to find the VALUE, and that is the
454
+ * axis the build must keep live.
440
455
  */
441
- export const TYPES_GENRE_TEMPLATE: "1. The graded instrument is the STRICT form of the language's type checker, regardless of what\n the repo's own scripts run. If the repo's script omits strictness, the close adds it.\n2. Tools are invoked so their binaries actually resolve \u2014 through the project's own package\n runner or language module runner, never a bare binary name.\n3. The stage skeleton, in first-red-wins order: changed-from-seed (guard) \u2192 typecheck (error\n count IN the target scope, baseline 0, lower-is-better) \u2192 typecheck-outside (error count\n OUTSIDE the target scope, baseline measured at seed, a ceiling \u2014 required whenever the job\n scopes to a subset of the tree; omitted only for whole-tree jobs) \u2192 tests-kept (a floor on\n tests that actually EXECUTED, baseline at seed, higher-is-better; a skipped or deselected\n test did not run and must not count) \u2192 suite-green (the suite exits clean AND reports zero\n failing tests \u2014 two assertions) \u2192 no-suppressions (guard).\n4. One population per stage \u2014 two structurally different counts never share a stage.\n5. The checker must judge the PATIENT's own tree: if imports could resolve to an installed or\n editable copy elsewhere, the environment is set so they resolve inside the patient.\n6. A number the tool did not report is unknown, never zero.";
456
+ export const TYPES_GENRE_TEMPLATE: "1. The STRICT form of the language's type checker is used only when a confirmed check \u2014 or,\n absent a confirm turn, the goal and interview answers \u2014 asks for strict typing; otherwise the\n checker runs at whatever strictness the repo's own scripts already use.\n2. Tools are invoked so their binaries actually resolve \u2014 through the project's own package\n runner or language module runner, never a bare binary name.\n3. The WORK stages \u2014 typecheck (error count IN the target scope, baseline 0, lower-is-better),\n typecheck-outside (error count OUTSIDE the target scope, baseline measured at seed, a\n ceiling \u2014 required whenever a typecheck stage is scoped to a subset of the tree), tests-kept\n (a floor on tests that actually EXECUTED, baseline at seed, higher-is-better; a skipped or\n deselected test did not run and must not count), and suite-green (the suite exits clean AND\n reports zero failing tests \u2014 two assertions) \u2014 are composed ONLY for the checks a confirmed\n plan asked for (or, absent a confirm turn, the goal and interview answers). The GUARDS \u2014\n changed-from-seed and no-suppressions \u2014 are composed always, first-red-wins:\n changed-from-seed before every WORK stage, no-suppressions last.\n4. One population per stage \u2014 two structurally different counts never share a stage.\n5. The checker must judge the PATIENT's own tree: if imports could resolve to an installed or\n editable copy elsewhere, the environment is set so they resolve inside the patient.\n6. A number the tool did not report is unknown, never zero.";
442
457
  export const GENRE_LANGUAGES: readonly string[];
443
458
  /**
444
459
  * THE GENRE, as data. The suppression batteries are the hand-written closes'
@@ -457,7 +472,7 @@ export const GENRE_LANGUAGES: readonly string[];
457
472
  */
458
473
  export const TYPES_GENRE: Readonly<{
459
474
  name: "TYPES";
460
- template: "1. The graded instrument is the STRICT form of the language's type checker, regardless of what\n the repo's own scripts run. If the repo's script omits strictness, the close adds it.\n2. Tools are invoked so their binaries actually resolve — through the project's own package\n runner or language module runner, never a bare binary name.\n3. The stage skeleton, in first-red-wins order: changed-from-seed (guard) → typecheck (error\n count IN the target scope, baseline 0, lower-is-better) → typecheck-outside (error count\n OUTSIDE the target scope, baseline measured at seed, a ceiling — required whenever the job\n scopes to a subset of the tree; omitted only for whole-tree jobs) → tests-kept (a floor on\n tests that actually EXECUTED, baseline at seed, higher-is-better; a skipped or deselected\n test did not run and must not count) → suite-green (the suite exits clean AND reports zero\n failing tests — two assertions) → no-suppressions (guard).\n4. One population per stage — two structurally different counts never share a stage.\n5. The checker must judge the PATIENT's own tree: if imports could resolve to an installed or\n editable copy elsewhere, the environment is set so they resolve inside the patient.\n6. A number the tool did not report is unknown, never zero.";
475
+ template: "1. The STRICT form of the language's type checker is used only when a confirmed check — or,\n absent a confirm turn, the goal and interview answers — asks for strict typing; otherwise the\n checker runs at whatever strictness the repo's own scripts already use.\n2. Tools are invoked so their binaries actually resolve — through the project's own package\n runner or language module runner, never a bare binary name.\n3. The WORK stages — typecheck (error count IN the target scope, baseline 0, lower-is-better),\n typecheck-outside (error count OUTSIDE the target scope, baseline measured at seed, a\n ceiling — required whenever a typecheck stage is scoped to a subset of the tree), tests-kept\n (a floor on tests that actually EXECUTED, baseline at seed, higher-is-better; a skipped or\n deselected test did not run and must not count), and suite-green (the suite exits clean AND\n reports zero failing tests — two assertions) — are composed ONLY for the checks a confirmed\n plan asked for (or, absent a confirm turn, the goal and interview answers). The GUARDS —\n changed-from-seed and no-suppressions — are composed always, first-red-wins:\n changed-from-seed before every WORK stage, no-suppressions last.\n4. One population per stage — two structurally different counts never share a stage.\n5. The checker must judge the PATIENT's own tree: if imports could resolve to an installed or\n editable copy elsewhere, the environment is set so they resolve inside the patient.\n6. A number the tool did not report is unknown, never zero.";
461
476
  languages: Readonly<{
462
477
  js: Readonly<{
463
478
  suppressions: readonly (Readonly<{
@@ -85,13 +85,16 @@ export function refuseLockedKind(kind: string): Refusal;
85
85
  * onPhase?: (phase: string, data?: any) => void,
86
86
  * onCall?: (call: {label: string, costUsd: number|null, unpricedRounds: number}) => void,
87
87
  * seedFn?: Function, scoutFn?: Function, listingFn?: Function,
88
- * authorFn?: Function, authorOpts?: object,
89
- * signerFix?: Function|any, proposeFn?: Function, compileOpts?: object}} o
88
+ * authorFn?: Function, authorOpts?: object, writeScope?: string[]|null,
89
+ * signerFix?: Function|any, proposeFn?: Function, compileOpts?: object,
90
+ * ask?: ((step: {kind: string, [k: string]: any}) => Promise<string|null>)|null,
91
+ * confirmGenerate?: Function|null, isRepo?: boolean,
92
+ * langResult?: {kind: string, candidates?: string[], [k: string]: any}|null}} o
90
93
  * @returns {Promise<{ok: boolean, refusal: Refusal|null, verdictType: string|null,
91
94
  * closeDecl: any, seedRef: string|null, authoring: any, judged: any, reds: Red[],
92
- * stop: string|null, cost: any, interview: any}>}
95
+ * stop: string|null, cost: any, interview: any, confirmed: any}>}
93
96
  */
94
- export function authorCloseForJob({ answers, repoPath, lang, verdictType, questions, generate, provider, seedRef, scout, listing, ceilingUsd, judgeModel, onPhase, onCall, seedFn, scoutFn, listingFn, authorFn, authorOpts, signerFix, proposeFn, compileOpts, }: {
97
+ export function authorCloseForJob({ answers, repoPath, lang, verdictType, questions, generate, provider, seedRef, scout, listing, ceilingUsd, writeScope, judgeModel, onPhase, onCall, seedFn, scoutFn, listingFn, authorFn, authorOpts, signerFix, proposeFn, compileOpts, ask, confirmGenerate, isRepo, langResult, }: {
95
98
  answers: Record<string | number, any>;
96
99
  repoPath?: string | null;
97
100
  lang: string;
@@ -115,9 +118,21 @@ export function authorCloseForJob({ answers, repoPath, lang, verdictType, questi
115
118
  listingFn?: Function;
116
119
  authorFn?: Function;
117
120
  authorOpts?: object;
121
+ writeScope?: string[] | null;
118
122
  signerFix?: Function | any;
119
123
  proposeFn?: Function;
120
124
  compileOpts?: object;
125
+ ask?: ((step: {
126
+ kind: string;
127
+ [k: string]: any;
128
+ }) => Promise<string | null>) | null;
129
+ confirmGenerate?: Function | null;
130
+ isRepo?: boolean;
131
+ langResult?: {
132
+ kind: string;
133
+ candidates?: string[];
134
+ [k: string]: any;
135
+ } | null;
121
136
  }): Promise<{
122
137
  ok: boolean;
123
138
  refusal: Refusal | null;
@@ -130,6 +145,7 @@ export function authorCloseForJob({ answers, repoPath, lang, verdictType, questi
130
145
  stop: string | null;
131
146
  cost: any;
132
147
  interview: any;
148
+ confirmed: any;
133
149
  }>;
134
150
  /**
135
151
  * Fold an authored close into the OPERATOR's own half of the spec.
@@ -259,6 +275,8 @@ export const REFUSAL_CATEGORY: "close-unauthorable";
259
275
  * carry. Named as data so the refusal and the fold can never disagree about
260
276
  * which half of the spec is which. */
261
277
  export const AUTHORED_SPEC_FIELDS: readonly string[];
278
+ export const CONFIRM_AUTHORED_FIELDS: readonly string[];
279
+ export const PLAIN_FOLDER_DEFERRED_FIELDS: readonly string[];
262
280
  export type Red = {
263
281
  code: string;
264
282
  path: string;
@@ -200,8 +200,12 @@ export function proposalTool(box: {
200
200
  execute: (args: any) => Promise<string>;
201
201
  };
202
202
  /**
203
- * The compile prompt. Q6's and Q7's answers travel VERBATIM — they are the whole
204
- * input, and paraphrasing a lay answer is how a rubric stops being the person's.
203
+ * The compile prompt. The Judge Examples answer travels VERBATIM — it is the
204
+ * whole input, and paraphrasing a lay answer is how a rubric stops being the
205
+ * person's. PRD item 33 M3 piece 3: this used to be two answers (Q6+Q7);
206
+ * `SOFTGREEN_JUDGE_EXAMPLES_KEY` is the ONE key both the interview and this
207
+ * compile now read, so a future renumbering of the form moves both readers
208
+ * together rather than drifting.
205
209
  *
206
210
  * The rulebook is rendered from `JUDGE_RULES` itself rather than restated, for
207
211
  * the reason the catalogue block is rendered from the catalogue: the prompt and
@@ -288,7 +292,7 @@ export function foldJudgedArtifacts(closeDecl: any, { card, cases, judgeModel }:
288
292
  judgeModel: string;
289
293
  }): any;
290
294
  /**
291
- * ONE compile call: the person's Q6 and Q7 in, a proposal out.
295
+ * ONE compile call: the person's Judge Examples answer in, a proposal out.
292
296
  *
293
297
  * It buys ONE artifact through ONE channel, and the malformed-emission ladder is
294
298
  * the SHARED one (`askStructured`) rather than a second copy — a second ladder is
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Detect a code job's language from its repository, $0, no provider.
3
+ *
4
+ * @param {string} sourcePath the Source path (a folder, or a file inside one)
5
+ * @returns {{kind: 'resolved', lang: string, dir: string, manifest: string} | {kind: 'language-unsupported', language: string, dir: string, manifest: string, refusal: LangRefusal} | {kind: 'no-code-job'} | {kind: 'ambiguous', candidates: string[], dir: string}}
6
+ */
7
+ export function detectLanguage(sourcePath: string): {
8
+ kind: "resolved";
9
+ lang: string;
10
+ dir: string;
11
+ manifest: string;
12
+ } | {
13
+ kind: "language-unsupported";
14
+ language: string;
15
+ dir: string;
16
+ manifest: string;
17
+ refusal: LangRefusal;
18
+ } | {
19
+ kind: "no-code-job";
20
+ } | {
21
+ kind: "ambiguous";
22
+ candidates: string[];
23
+ dir: string;
24
+ };
25
+ export type Red = {
26
+ code: string;
27
+ path: string;
28
+ detail: string;
29
+ [k: string]: any;
30
+ };
31
+ export type LangRefusal = {
32
+ kind: "request-red";
33
+ verb: string | null;
34
+ path: string;
35
+ detail: string;
36
+ options: string[];
37
+ red: Red;
38
+ };
package/types/index.d.ts CHANGED
@@ -1,5 +1,7 @@
1
1
  export { makeSpine } from "./spine.js";
2
2
  export { runPlan } from "./planrun.js";
3
+ export { workBranchName } from "./workbranch.js";
4
+ export { answerReviewDoor } from "./reviewdoor.js";
3
5
  export { main as cliMain } from "./cli.js";
4
6
  export { ralph, runClose, runStages, CLOSE_FAULTS } from "./ralph.js";
5
7
  export { globToPrefix, scanSecrets } from "./validate.js";
@@ -7,26 +9,24 @@ export { validateJob, jobSpecHash, checkApproval, checkMenu, resolveWorkerModel,
7
9
  export { validatePlan, stageClose, EXIT_TYPES, MAX_EXITS_PER_STEP, MAX_PLAN_STEPS, WRITE_VERBS, closeStagesOf } from "./plan.js";
8
10
  export { snapshotScope, evalExits } from "./exits.js";
9
11
  export { runJob, ACCOUNTED_ROUND_TYPES } from "./run.js";
10
- export { BRIDGE_SCHEMA, validateBridge, deriveStatus, listingRow, loadGate, mintBridge, appendGreen, appendRed, loadBridge, loadRegistry, saveBridge, makeRegistry, registryExists, QUARANTINED_VERDICTS, QUARANTINED_CODE, quarantinesCredit, newestEligibleVersion, reuseEligibility, recordDoor } from "./bridges.js";
12
+ export { BRIDGE_SCHEMA, validateBridge, deriveStatus, listingRow, loadGate, mintBridge, appendGreen, appendRed, loadBridge, loadRegistry, saveBridge, makeRegistry, registryExists, QUARANTINED_VERDICTS, quarantinesCredit, newestEligibleVersion, reuseEligibility, recordDoor } from "./bridges.js";
11
13
  export { renderListing, selectionPrompt } from "./selection.js";
12
- export { validateEnvelope, resolveTrySpec, resolveReuse, reuseSpecHash, selectBridge, runReuse, REUSE_GRADED_RED, readResume, resumeTreeGate, CHECKPOINT_OUTCOMES, PAUSE_TTL_MS, checkpointAgeGate, applyDoorDecision, writeRunGreenRow, shapeForkName } from "./reuse.js";
14
+ export { validateEnvelope, resolveTrySpec, resolveReuse, reuseSpecHash, selectBridge, runReuse, REUSE_GRADED_RED, readResume, resumeTreeGate, CHECKPOINT_OUTCOMES, PAUSE_TTL_MS, checkpointAgeGate, applyDoorDecision, writeRunGreenRow } from "./reuse.js";
13
15
  export { classifyIncidents, foldLedger, ledgerDeltas, updateLedger, LEDGER_CLASSES, VOUCHED_RATE_SOURCES, rateProvenance, spendProvenance } from "./ledger.js";
14
- export { workBranchName, prepareWorkBranch, WORK_BRANCH_PREFIX, WORK_BRANCH_RE } from "./workbranch.js";
15
- export { createReadShim, wrapReadTool, readShimArm, readShimStrategy, READ_SHIM_CAP, READ_SHIM_STRATEGY, READ_SHIM_DIFF_STRATEGY, READ_SHIM_ARMS } from "./readshim.js";
16
- export { runDeclaredClose, runStage, seedRead, seedAtHead, seedListing, changedSet, makeSeedTrees, LIVE_KINDS, STOP_FAULTS, JUDGED_MARKER, EXIT_GREEN, EXIT_RED, EXIT_STOP, HUMAN_DECISIONS, SEED_EXEMPT_KINDS, JUDGED_FLOOR_KIND, normalizeHumanRuling, resolveHumanRuling, JUDGE_ATTEMPTS, MAX_JUDGED_PATHS, REVIEW_DOOR, DOOR_OPEN_OUTCOMES, REVIEW_DOOR_CLASSES, doorOpens, mechanicalStages } from "./kinds.js";
17
- export { answerReviewDoor, doorRecordOf, doorAgeGate } from "./reviewdoor.js";
18
- export { JUDGE_MODEL, resolveJudge, JUDGE_MAX_TOKENS, JUDGE_RULES, JUDGE_RULE_IDS, LOCATE_AXES, LOCATE_LABEL, validateCard, validateFacts, locatePrompt, runLocate, decide, defaultJudgeLoop, CALIBRATION_SIZE, CASE_VERDICTS, validateCalibrationSet, validateJudgedArtifacts, expectedOf } from "./judged.js";
19
- export { proposeJudgedArtifacts, signJudgedArtifacts, foldJudgedArtifacts, proposalSchema, proposalTool, cardCasesPrompt, PROPOSAL_TOOL_NAME, PROPOSAL_LABEL, MAX_PROPOSAL_RETRIES, COMPILE_SYSTEM } from "./cardauthor.js";
20
- export { runCalibration, compareExpectation, factsResist, artifactHash, INJECTION_LOCATE_BATTERY, INJECTION_CARD, CALIBRATION_LABEL, CASUALTY_AXES } from "./calibrate.js";
21
- export { validateDeclaration, normalizeDeclaration, KIND_CATALOGUE, CATALOGUE_KINDS, CATALOGUE_LIVE_KINDS, LOCKED_KINDS, TYPES_GENRE, TYPES_GENRE_TEMPLATE, GENRE_LANGUAGES, genreEnv, genreOwnedEnvNames, genreInstruments, DIRECTIONS, BASELINES, MAX_STAGES, VERDICT_CLASSES, LOCKED_CLASSES, LIVE_CLASSES, CLASS_BATTERIES, classGuards, classMenu, closeCeiling, NEVER_OFFERED_KINDS } from "./authoring.js";
22
- export { runAuthorScout, buildSeedListing, seedFileList, classifySurvey, AUTHOR_SCOUT_VERBS, SCOUT_ATTEMPTS, SURVEY_CAUSES, SCOUT_RETRY_CAUSES } from "./authorscout.js";
23
- export { scrubRaw, RAW_PERSIST_MAX, RAW_TRIM_MARKER } from "./text.js";
24
- export { authorClose, authorPrompt, declarationSchema, makeLoopGenerate, MAX_REVISIONS, QUESTION_SETS, GREEN_QUESTIONS, SOFTGREEN_QUESTIONS, CLASS_STATEMENTS, questionsFor, requiredAnswersFor } from "./authorflow.js";
25
- export { validateCloseDecl, runDeclaredStages, declaredStages, isDeclaredClose, guardNames, closeGrade, DECLARED_GENRES, DECLARED_GAP_PREFIX, DECLARED_GAP_KEEP, DECLARED_CLOSE_CLASSES, CLOSE_DECL_FIELDS, HUMAN_PAUSE, HITL_PAUSE, HITL_DECISION_RED, HUMAN_CHECKPOINTS } from "./declaredclose.js";
16
+ export { readShimArm, READ_SHIM_CAP, READ_SHIM_STRATEGY, READ_SHIM_DIFF_STRATEGY } from "./readshim.js";
17
+ export { runDeclaredClose, runStage, seedRead, seedAtHead, LIVE_KINDS, STOP_FAULTS, JUDGED_MARKER, HUMAN_DECISIONS, SEED_EXEMPT_KINDS, normalizeHumanRuling, resolveHumanRuling, JUDGE_ATTEMPTS, MAX_JUDGED_PATHS, DOOR_OPEN_OUTCOMES, REVIEW_DOOR_CLASSES, doorOpens, mechanicalStages } from "./kinds.js";
18
+ export { JUDGE_MODEL, resolveJudge, JUDGE_RULES, JUDGE_RULE_IDS, runLocate, decide, CALIBRATION_SIZE, validateCalibrationSet, expectedOf } from "./judged.js";
19
+ export { proposeJudgedArtifacts, signJudgedArtifacts, foldJudgedArtifacts } from "./cardauthor.js";
20
+ export { runCalibration, INJECTION_LOCATE_BATTERY, CASUALTY_AXES } from "./calibrate.js";
21
+ export { validateDeclaration, KIND_CATALOGUE, LOCKED_KINDS, genreEnv, genreInstruments, LOCKED_CLASSES, CLASS_BATTERIES, classGuards, closeCeiling, NEVER_OFFERED_KINDS } from "./authoring.js";
22
+ export { runAuthorScout, buildSeedListing, AUTHOR_SCOUT_VERBS, SCOUT_ATTEMPTS, SCOUT_RETRY_CAUSES } from "./authorscout.js";
23
+ export { scrubRaw, RAW_PERSIST_MAX } from "./text.js";
24
+ export { authorClose, MAX_REVISIONS, questionsFor, requiredAnswersFor } from "./authorflow.js";
25
+ export { validateCloseDecl, runDeclaredStages, declaredStages, HUMAN_PAUSE } from "./declaredclose.js";
26
26
  export { runBehaviour, formatBehaviour } from "./behaviour.js";
27
27
  export { replayRun, formatReplay } from "./replay.js";
28
- export { runInterview, authorCloseForJob, assembleSpec, prepareSigning, refusalEvents, refuseLockedKind, GENRE, REFUSAL_LIB, REFUSAL_CATEGORY, AUTHORED_SPEC_FIELDS } from "./authorjob.js";
28
+ export { runInterview, authorCloseForJob, assembleSpec, prepareSigning, refusalEvents, refuseLockedKind, AUTHORED_SPEC_FIELDS } from "./authorjob.js";
29
29
  export { PROMPT_REGISTERS, isPromptFile } from "./promptregisters.js";
30
30
  export { exportBundle, bundleHash, readBundle, resolveBundleSpec, checkEnvelope, bless, verifyBlessing, appendHistory, checkBundleDeps } from "./bundle.js";
31
31
  export { readCloseScripts, checkCloseAbsolutePaths, absolutePathLiteralsOf, hashCloseScriptBytes, checkCloseByteSignature, checkStageByteSignature, signCloseScripts } from "./close-integrity.js";
32
- export { prepareSource, proveDestination, copyOut, readSourceManifest, frontDoorFromManifest } from "./source.js";
32
+ export { prepareSource, proveDestination, copyOut, readSourceManifest, frontDoorFromManifest, datedDestination, pickDelivery } from "./source.js";
package/types/job.d.ts CHANGED
@@ -209,8 +209,9 @@ export const CADENCE_UNITS: readonly string[];
209
209
  * rows on the cost axis, and budgets sized for one do not transfer. */
210
210
  /** PRD item 28 (2026-09-06/09-09): `openai-api` — an OpenAI-shaped provider,
211
211
  * constructed via `src/providers.js`'s factory. hamr's ruling (PRD 30.7):
212
- * `deepseek-chat` is THE secondary provider (one, not a menu of half-tested
213
- * models); the factory's tier table is where that lives, not this menu.
212
+ * `deepseek-flash` is THE secondary provider (one, not a menu of half-tested
213
+ * models; F171, item 34 L16: swapped from the retired `deepseek-chat`); the
214
+ * factory's tier table is where that lives, not this menu.
214
215
  *
215
216
  * PRD item 31.3 (hamr, 2026-09-09: *"anthropic, openai, gemini drop ollama for
216
217
  * now"*): `gemini-api` joins. That is bareloop matching bare-agent's own
package/types/kinds.d.ts CHANGED
@@ -50,12 +50,14 @@ export function doorOpens(job: any, reviewDoor?: boolean | null): boolean;
50
50
  */
51
51
  export function mechanicalStages(stages: any[] | null | undefined): any[];
52
52
  /**
53
- * git, read-only, in a repository. Faults come back as `ok:false` with the
54
- * stderr — a caller decides whether that is a stop.
53
+ * git, in a repository (reads by every caller in this file; `src/source.js`
54
+ * also uses it for the hidden-git seed's `init`/`add`/`commit`). Faults come
55
+ * back as `ok:false` with the stderr — a caller decides whether that is a
56
+ * stop.
55
57
  *
56
58
  * Exported (PRD item 33/M2, `src/source.js`) so the hidden-git seed a plain
57
59
  * folder/file/URL job runs against goes through the SAME spawn primitive as
58
- * every other git read in this file — a second `spawn('git', …)` for the
60
+ * every other git call in this file — a second `spawn('git', …)` for the
59
61
  * source front door would be a second instrument with its own timeout and
60
62
  * buffer ceiling to keep in sync with this one.
61
63
  * @param {string} cwd @param {string[]} args
@@ -108,10 +108,17 @@ export const ANTHROPIC_TIER_MODELS: Readonly<{
108
108
  * of a single-model provider, not a second model masquerading as `haiku`.
109
109
  * If DeepSeek (or a future openai-api entrant) ever ships a genuinely
110
110
  * cheaper tier model, this is where it would earn its own `haiku` id.
111
+ *
112
+ * MODEL SWAP (F171, item 34 L16, hamr 2026-09-13): `deepseek-chat` is no
113
+ * longer served — a live `/models` call returned only `deepseek-flash` and
114
+ * `deepseek-v4-pro`. Switched to `deepseek-flash` (DeepSeek V4.1). F149's
115
+ * `legacyMaxTokens` measurement (asked 64 output tokens, got 665/608) was
116
+ * taken on `deepseek-chat` and is UNVERIFIED on `deepseek-flash` — kept on
117
+ * below as the fail-safe (over-capping direction is safe; see F171).
111
118
  */
112
119
  export const OPENAI_TIER_MODELS: Readonly<{
113
- sonnet: "deepseek-chat";
114
- haiku: "deepseek-chat";
120
+ sonnet: "deepseek-flash";
121
+ haiku: "deepseek-flash";
115
122
  }>;
116
123
  /**
117
124
  * Gemini (PRD item 31.3, hamr 2026-09-09: "anthropic, openai, gemini drop
package/types/source.d.ts CHANGED
@@ -1,3 +1,45 @@
1
+ /**
2
+ * Walk from `startDir` upward — inclusive — to the NEAREST ancestor whose own
3
+ * directory carries a `.git` entry, whether that entry is a real repo
4
+ * directory or a linked-worktree/submodule FILE (the caller decides what a
5
+ * file means for it). `null` when no ancestor up to the filesystem root has
6
+ * one. ONE helper for "find the repo boundary walking up", shared by
7
+ * {@link looksLikeRepoSource} and `prepareSource`'s own authoritative routing
8
+ * below (PRD item 33 M3 ruling 2 addendum, 2026-09-13: a Source that is a
9
+ * SUBFOLDER inside a git repo is a repo job, not a plain-folder one — the
10
+ * boundary is the nearest ancestor's `.git`, never only a `.git` sitting
11
+ * directly inside Source itself) and reused by `src/detectlang.js`'s own
12
+ * upward walk (`walkChain`), which only needs the boundary directory, not
13
+ * which kind of `.git` it is — never three copies of this rule.
14
+ * @param {string} startDir an absolute directory to start the walk FROM
15
+ * @returns {{dir: string, isFile: boolean}|null}
16
+ */
17
+ export function nearestGitAncestor(startDir: string): {
18
+ dir: string;
19
+ isFile: boolean;
20
+ } | null;
21
+ /**
22
+ * A CHEAP peek (a stat, never a read) at whether `source` is going to turn
23
+ * out to be a REPO source — the ONE rule for that question, shared by
24
+ * `prepareSource` (which routes the destination check below before the real
25
+ * walk further down decides authoritatively) and `scripts/run-interview.mjs`
26
+ * (PRD item 33 M3 ruling 2), which has to know BEFORE Source is even frozen
27
+ * whether Destination fills the write fence or a delivery directory. Never a
28
+ * second, hand-typed copy of this test: a URL is never a repo; a local path
29
+ * is a repo source only when it is a DIRECTORY, and the NEAREST ancestor
30
+ * (Source itself, or an ancestor above it — ruling 2's 2026-09-13 addendum)
31
+ * carries a `.git` DIRECTORY. A nearest `.git` that is a FILE (a linked
32
+ * worktree or a submodule) is NOT repo-like here — the real walk below
33
+ * refuses `source-is-linked-worktree` for that case, and this peek must
34
+ * agree closely enough to route the destination question correctly rather
35
+ * than offer the write-fence wording for a source that is about to be
36
+ * refused. An unreadable path is reported properly by the real walk; this
37
+ * peek only has to agree with that walk closely enough to route correctly,
38
+ * never to replace it.
39
+ * @param {string} source
40
+ * @returns {boolean}
41
+ */
42
+ export function looksLikeRepoSource(source: string): boolean;
1
43
  /**
2
44
  * Build the frozen source tree — `<into>/tree/input/…` (the copy), `<into>/tree/output/`
3
45
  * (empty, where the run writes), `<into>/source.json` (the manifest, OUTSIDE