memhtml 0.7.0 → 0.7.2
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/{dist-D1wH0oJ0.mjs → dist-xlKgJuWn.mjs} +262 -26
- package/dist/dist-xlKgJuWn.mjs.map +1 -0
- package/dist/memhtml-mcp.mjs +2 -2
- package/dist/memhtml-mcp.mjs.map +1 -1
- package/dist/memhtml.mjs +2 -2
- package/dist/memhtml.mjs.map +1 -1
- package/package.json +1 -1
- package/dist/dist-D1wH0oJ0.mjs.map +0 -1
|
@@ -4346,6 +4346,18 @@ const parseLsTree = (output) => output.split("\0").filter((row) => row !== "").f
|
|
|
4346
4346
|
path: row.slice(tab + 1)
|
|
4347
4347
|
}];
|
|
4348
4348
|
});
|
|
4349
|
+
/**
|
|
4350
|
+
* A bare NUL-terminated path list, which is what every `-z` path-only git output is.
|
|
4351
|
+
*
|
|
4352
|
+
* One function for the shape rather than one `split` per call site, because the trailing NUL is what
|
|
4353
|
+
* makes the naive read wrong: `"a\0b\0"` splits into three fields, the last empty, so a caller that
|
|
4354
|
+
* forgets the filter gets an empty string in its path set. `diff-tree --name-only -z` and
|
|
4355
|
+
* `diff --name-only --diff-filter=U -z` are both this shape.
|
|
4356
|
+
*
|
|
4357
|
+
* `-z` is also what removes the escaping question: under newline framing git applies `core.quotePath`
|
|
4358
|
+
* and a path holding a non-ASCII byte arrives as `"caf\303\251.html"`, quotes included.
|
|
4359
|
+
*/
|
|
4360
|
+
const parseNulPathList = (output) => output.split("\0").filter((path) => path !== "");
|
|
4349
4361
|
/** The status letters git emits, mapped to the names this package uses. */
|
|
4350
4362
|
const CHANGE_KINDS = {
|
|
4351
4363
|
A: "added",
|
|
@@ -4639,10 +4651,15 @@ var GitFailure = class extends Schema.TaggedError()("GitFailure", {
|
|
|
4639
4651
|
}) {};
|
|
4640
4652
|
const Git = Context.Service("memhtml/Git");
|
|
4641
4653
|
/**
|
|
4642
|
-
* Environment for every git call.
|
|
4643
|
-
*
|
|
4644
|
-
*
|
|
4645
|
-
*
|
|
4654
|
+
* Environment for every git call. `GIT_TERMINAL_PROMPT=0` keeps a credential prompt from hanging a
|
|
4655
|
+
* headless indexer forever, `GIT_OPTIONAL_LOCKS=0` stops a read from taking the index lock, and
|
|
4656
|
+
* `LC_ALL=C` pins the message locale so a parsed word is the same word everywhere.
|
|
4657
|
+
*
|
|
4658
|
+
* **A user's own config IS read.** Nothing here neutralizes `~/.gitconfig`, so every call whose output
|
|
4659
|
+
* a caller parses states its format with explicit flags rather than inheriting a default: `-z` against
|
|
4660
|
+
* `core.quotePath`, `-M` and `--no-renames` against `diff.renames`, `--porcelain=v2` against every
|
|
4661
|
+
* status shorthand. A format a developer's machine and CI could disagree about is a format this
|
|
4662
|
+
* service does not rely on.
|
|
4646
4663
|
*/
|
|
4647
4664
|
const GIT_ENV = {
|
|
4648
4665
|
GIT_TERMINAL_PROMPT: "0",
|
|
@@ -4651,6 +4668,8 @@ const GIT_ENV = {
|
|
|
4651
4668
|
};
|
|
4652
4669
|
/** 64 MiB. A `cat-file --batch` over a whole corpus is the one call with a large stdout. */
|
|
4653
4670
|
const MAX_BUFFER = 67108864;
|
|
4671
|
+
/** A full 40-hex object name, which is the only form {@link GitShape.diffTreeNames} accepts. */
|
|
4672
|
+
const FULL_OBJECT_NAME = /^[0-9a-f]{40}$/;
|
|
4654
4673
|
/**
|
|
4655
4674
|
* Spawn git and collect its output as bytes.
|
|
4656
4675
|
*
|
|
@@ -4735,6 +4754,50 @@ const makeGit = (root) => ({
|
|
|
4735
4754
|
from,
|
|
4736
4755
|
to
|
|
4737
4756
|
]).pipe(Effect.map((result) => parseDiffNameStatus(text$1(result)))),
|
|
4757
|
+
/**
|
|
4758
|
+
* Dropping any of the first five flags makes the answer SILENTLY smaller or dirtier rather than an
|
|
4759
|
+
* error, which is why each has its own case in `git.test.ts`. Measured against git 2.50.1 on
|
|
4760
|
+
* 2026-08-26:
|
|
4761
|
+
*
|
|
4762
|
+
* - `--stdin`, so a range of hundreds of commits costs one process;
|
|
4763
|
+
* - `-r`, or the answer is the changed TREE (`areas`) instead of the blobs under it;
|
|
4764
|
+
* - `-m`, or a merge commit reports ZERO paths (empty without, its paths with, and byte-identical
|
|
4765
|
+
* on a single-parent commit, so it is free);
|
|
4766
|
+
* - `--root`, or a root commit reports ZERO paths (0 without, its whole tree with);
|
|
4767
|
+
* - `--no-commit-id`, or each commit's own 40-hex sha arrives as a line among the paths;
|
|
4768
|
+
* - `-z`, which frames on NUL and disables `core.quotePath` — under newline framing a path
|
|
4769
|
+
* holding a non-ASCII byte arrives as `"areas/x/caf\303\251.html"`, quoted and escaped, and
|
|
4770
|
+
* equals nothing a caller compares it to.
|
|
4771
|
+
*
|
|
4772
|
+
* `--no-renames` is INERT here and is declared anyway. `diff-tree` detects no renames in this form
|
|
4773
|
+
* whatever the config says — probed under `diff.renames` unset, `true`, and `copies`, all three
|
|
4774
|
+
* reporting both paths of a `git mv` — so the flag pins that default rather than producing it, the
|
|
4775
|
+
* same reason the calls above spell `-z` and `--porcelain=v2`. What holds the behavior is the test,
|
|
4776
|
+
* not the flag: a caller unioning paths needs the path that went away as much as the one that
|
|
4777
|
+
* arrived, and `git diff --name-only` (which DOES detect renames) reports only the destination.
|
|
4778
|
+
*
|
|
4779
|
+
* **An abbreviated sha is refused rather than passed through**, because git echoes the short name
|
|
4780
|
+
* as its own output line and reports no diff at all — with `--no-commit-id` set, a caller would
|
|
4781
|
+
* receive one bogus path and no real ones, at exit 0. `%H` from `logTrailers` is already full, so
|
|
4782
|
+
* the check costs nothing and closes the one input that fails quietly.
|
|
4783
|
+
*/
|
|
4784
|
+
diffTreeNames: (commits) => {
|
|
4785
|
+
if (commits.find((sha) => !FULL_OBJECT_NAME.test(sha)) !== void 0) return Effect.fail(GitFailure.make({
|
|
4786
|
+
command: "diff-tree",
|
|
4787
|
+
exitCode: null
|
|
4788
|
+
}));
|
|
4789
|
+
return commits.length === 0 ? Effect.succeed([]) : git(root, "diff-tree", [
|
|
4790
|
+
"diff-tree",
|
|
4791
|
+
"-r",
|
|
4792
|
+
"-m",
|
|
4793
|
+
"--root",
|
|
4794
|
+
"--stdin",
|
|
4795
|
+
"--no-commit-id",
|
|
4796
|
+
"--name-only",
|
|
4797
|
+
"-z",
|
|
4798
|
+
"--no-renames"
|
|
4799
|
+
], { stdin: `${commits.join("\n")}\n` }).pipe(Effect.map((result) => parseNulPathList(text$1(result))));
|
|
4800
|
+
},
|
|
4738
4801
|
statusPorcelainV2: () => git(root, "status", [
|
|
4739
4802
|
"status",
|
|
4740
4803
|
"--porcelain=v2",
|
|
@@ -4811,7 +4874,7 @@ const makeGit = (root) => ({
|
|
|
4811
4874
|
]);
|
|
4812
4875
|
return {
|
|
4813
4876
|
merged: false,
|
|
4814
|
-
conflicted: text$1(unmerged)
|
|
4877
|
+
conflicted: parseNulPathList(text$1(unmerged))
|
|
4815
4878
|
};
|
|
4816
4879
|
}),
|
|
4817
4880
|
mergeAbort: () => git(root, "merge-abort", ["merge", "--abort"]).pipe(Effect.asVoid),
|
|
@@ -10009,6 +10072,47 @@ const TRAILER_RUN = "Memhtml-Run";
|
|
|
10009
10072
|
const TRAILER_PHASE = "Memhtml-Phase";
|
|
10010
10073
|
const TRAILER_COUNTS = "Memhtml-Counts";
|
|
10011
10074
|
/**
|
|
10075
|
+
* Phases whose commit is a UNIFORM SWEEP: one head stamp applied to every eligible file by a rule,
|
|
10076
|
+
* carrying no per-file decision and authoring no edge.
|
|
10077
|
+
*
|
|
10078
|
+
* This is the distinction `placement-triage`'s this-run guard turns on. That guard refuses to move a
|
|
10079
|
+
* file another phase wrote on this branch, because a move would fold that phase's edit into a rename
|
|
10080
|
+
* a reviewer reads somewhere else. A sweep has no edit to fold: `confidence-decay` restamps
|
|
10081
|
+
* `memhtml-confidence` and `memhtml-updated` on every eligible active file, and its value is a
|
|
10082
|
+
* mechanical function of the value already in the file (`decayConfidence`), so nothing about it is
|
|
10083
|
+
* invalidated by the file being at a different path afterwards. It is also the WIDEST commit in a
|
|
10084
|
+
* run, so a guard that counts it pins essentially the whole corpus and the phase downstream refuses
|
|
10085
|
+
* essentially everything (issue #81).
|
|
10086
|
+
*
|
|
10087
|
+
* **The list enumerates the sweeps, so a phase absent from it PINS.** The two mistakes cost
|
|
10088
|
+
* different amounts. A phase wrongly treated as a sweep lets placement move a file it just wrote —
|
|
10089
|
+
* a committed href that dangles, or a decision folded into a rename — while a phase wrongly pinning
|
|
10090
|
+
* costs one night's yield on a file that is still a candidate tomorrow. So membership is an explicit
|
|
10091
|
+
* claim and the default is the recoverable side.
|
|
10092
|
+
*
|
|
10093
|
+
* A phase that calls a model is never a member: a model answer is a per-file decision. The two lists
|
|
10094
|
+
* are asserted disjoint in `units.test.ts`.
|
|
10095
|
+
*
|
|
10096
|
+
* The near-misses, because the absences carry the rule:
|
|
10097
|
+
*
|
|
10098
|
+
* - `reprieve` writes only head metas too, and is still out. `memhtml-valid-until` plus
|
|
10099
|
+
* `memhtml-reprieves` IS a per-file retention decision, and a reviewer reads it at the path it was
|
|
10100
|
+
* decided on. Its volume is bounded to the files whose TTL passed, so membership would buy almost
|
|
10101
|
+
* no reach against that.
|
|
10102
|
+
* - `person-links` and `edge-typing` splice `<link>` elements, which leave the article's bytes — and
|
|
10103
|
+
* therefore its content hash — identical. So the meaning of a change, not its width, is what
|
|
10104
|
+
* decides membership: a hash-based rule would release exactly these two, and placement's inbound
|
|
10105
|
+
* href rewrite reads the INDEX, which no phase refreshes mid-run, so an edge authored this run is
|
|
10106
|
+
* invisible to it and the move would leave the href dangling.
|
|
10107
|
+
* - `preflight` and `relationship-mining` are in {@link NON_COMMITTING_PHASES}, so no commit of
|
|
10108
|
+
* theirs can appear in a range; `integrity`, `state-export`, and `report` run after
|
|
10109
|
+
* `placement-triage`, so theirs cannot either. Membership for any of the five would be a claim
|
|
10110
|
+
* nothing can exercise.
|
|
10111
|
+
*/
|
|
10112
|
+
const SWEEP_PHASES = ["confidence-decay"];
|
|
10113
|
+
/** True when a phase's commit is a uniform sweep. See {@link SWEEP_PHASES}. */
|
|
10114
|
+
const isSweepPhase = (phase) => SWEEP_PHASES.includes(phase);
|
|
10115
|
+
/**
|
|
10012
10116
|
* Where a run's ledger lives: beside its report, under the same run-id-to-filename rule.
|
|
10013
10117
|
*
|
|
10014
10118
|
* `/` is not legal in a filename and a run id is `sleep/<date>`, so the separator becomes a hyphen —
|
|
@@ -15743,6 +15847,84 @@ const personLinks = (env) => Effect.gen(function* () {
|
|
|
15743
15847
|
};
|
|
15744
15848
|
});
|
|
15745
15849
|
|
|
15850
|
+
//#endregion
|
|
15851
|
+
//#region packages/sleep/dist/touched.js
|
|
15852
|
+
/**
|
|
15853
|
+
* True when a commit's `Memhtml-Phase` trailer says every phase that made it is a sweep.
|
|
15854
|
+
*
|
|
15855
|
+
* **A missing, empty, or unrecognized value pins.** The trailer is what identifies a sweep, so the
|
|
15856
|
+
* absence of the identification cannot grant the exemption — an operator's own mid-run commit, a
|
|
15857
|
+
* commit stamped with a phase name this version does not know, and a forged value that is not a phase
|
|
15858
|
+
* all land on the conservative side. `every` rather than `some` for the same reason: a commit
|
|
15859
|
+
* claiming two phases is exempt only if neither of them decided anything.
|
|
15860
|
+
*/
|
|
15861
|
+
const isSweepCommit = (values) => values.length > 0 && values.every((value) => isSleepPhase(value) && isSweepPhase(value));
|
|
15862
|
+
/**
|
|
15863
|
+
* The paths this run wrote, excluding the sweeps.
|
|
15864
|
+
*
|
|
15865
|
+
* **Paths come back exactly as git spells them, with no normalization, and that is deliberate.**
|
|
15866
|
+
* `diff-tree --name-only -z` emits repo-root-relative, `/`-separated, unquoted paths with no leading
|
|
15867
|
+
* slash — the same spelling the `files` table holds, because the indexer derived those from
|
|
15868
|
+
* `git ls-tree`. `normalizePath` strips leading slashes, collapses doubled ones, and trims a trailing
|
|
15869
|
+
* one, so against this output it is a no-op, and a no-op called for its name reads as a guarantee
|
|
15870
|
+
* nobody checks. What holds the two spellings in agreement is a test that moves a real file after a
|
|
15871
|
+
* real phase wrote it.
|
|
15872
|
+
*
|
|
15873
|
+
* The set legitimately holds paths that are not memories — `edge-typing` stages its pending-marks
|
|
15874
|
+
* ledger into its own commit — and they are inert, because a caller only ever asks whether a
|
|
15875
|
+
* candidate's own path is in it.
|
|
15876
|
+
*
|
|
15877
|
+
* `baseSha === ""` is an empty set rather than a scan of all of `HEAD`. Over `HEAD` a previously
|
|
15878
|
+
* merged run's trailers name every phase, so the set would become every file every past sleep ever
|
|
15879
|
+
* touched: the same over-pinning, one release further back. With no base there is no run to bound, and
|
|
15880
|
+
* production always has one because `preflight` requires a commit and a clean tree.
|
|
15881
|
+
*/
|
|
15882
|
+
const touchedThisRun = (git, baseSha) => Effect.gen(function* () {
|
|
15883
|
+
if (baseSha === "") return {
|
|
15884
|
+
kind: "scoped",
|
|
15885
|
+
paths: /* @__PURE__ */ new Set(),
|
|
15886
|
+
commits: 0,
|
|
15887
|
+
sweeps: 0
|
|
15888
|
+
};
|
|
15889
|
+
const range = `${baseSha}..HEAD`;
|
|
15890
|
+
const read = yield* Effect.result(git.logTrailers(range, TRAILER_PHASE));
|
|
15891
|
+
if (Result.isFailure(read)) {
|
|
15892
|
+
yield* Effect.logWarning(`sleep.touched could not read ${TRAILER_PHASE} over ${range}; pinning the whole range`);
|
|
15893
|
+
return yield* widen(git, baseSha);
|
|
15894
|
+
}
|
|
15895
|
+
const records = read.success;
|
|
15896
|
+
const semantic = records.flatMap((record) => isSweepCommit(record.values) ? [] : [record.sha]);
|
|
15897
|
+
const diffed = yield* Effect.result(git.diffTreeNames(semantic));
|
|
15898
|
+
if (Result.isFailure(diffed)) {
|
|
15899
|
+
yield* Effect.logWarning(`sleep.touched could not diff ${String(semantic.length)} commit(s) of ${range}; pinning the whole range`);
|
|
15900
|
+
return yield* widen(git, baseSha);
|
|
15901
|
+
}
|
|
15902
|
+
return {
|
|
15903
|
+
kind: "scoped",
|
|
15904
|
+
paths: new Set(diffed.success),
|
|
15905
|
+
commits: semantic.length,
|
|
15906
|
+
sweeps: records.length - semantic.length
|
|
15907
|
+
};
|
|
15908
|
+
});
|
|
15909
|
+
/**
|
|
15910
|
+
* Every path the range touched, as the fallback when the scoped read could not be made.
|
|
15911
|
+
*
|
|
15912
|
+
* Through `diffNameStatus`, which detects renames and reports the pre-move path in `fromPath`. BOTH
|
|
15913
|
+
* sides go in: this set exists to be conservative, and a `git mv`'s source is a path a phase wrote as
|
|
15914
|
+
* surely as its destination is.
|
|
15915
|
+
*/
|
|
15916
|
+
const widen = (git, baseSha) => git.diffNameStatus(baseSha, "HEAD").pipe(Effect.map((changes) => {
|
|
15917
|
+
const paths = /* @__PURE__ */ new Set();
|
|
15918
|
+
for (const change of changes) {
|
|
15919
|
+
paths.add(change.path);
|
|
15920
|
+
if (change.fromPath !== null) paths.add(change.fromPath);
|
|
15921
|
+
}
|
|
15922
|
+
return {
|
|
15923
|
+
kind: "widened",
|
|
15924
|
+
paths
|
|
15925
|
+
};
|
|
15926
|
+
}), Effect.orElseSucceed(() => ({ kind: "unknown" })));
|
|
15927
|
+
|
|
15746
15928
|
//#endregion
|
|
15747
15929
|
//#region packages/sleep/dist/phases/placement-triage.js
|
|
15748
15930
|
/**
|
|
@@ -15768,12 +15950,22 @@ const personLinks = (env) => Effect.gen(function* () {
|
|
|
15768
15950
|
* `archive/`, and any path outside the PARA buckets;
|
|
15769
15951
|
* - tasks never move (excluded from the scan AND re-checked per row, matching the double guard
|
|
15770
15952
|
* task-detection carries);
|
|
15771
|
-
* - a file
|
|
15772
|
-
*
|
|
15773
|
-
*
|
|
15953
|
+
* - a file a phase whose write carries a DECISION already touched this run never moves, read from
|
|
15954
|
+
* the diffs of the run's own commits whose `Memhtml-Phase` trailer is not in `SWEEP_PHASES` — a
|
|
15955
|
+
* `mv` of a path compress just archived would fail, and one of a path a phase stamped a link into
|
|
15956
|
+
* would tear that phase's edit out of the commit a reviewer reads it in. A SWEEP is exempt:
|
|
15957
|
+
* `confidence-decay` restamps every eligible file in the corpus by a rule, so counting it pins
|
|
15958
|
+
* essentially every candidate and this phase refuses essentially everything (issue #81). An
|
|
15959
|
+
* unreadable trailer or diff widens the set to the whole range instead of emptying it, and a
|
|
15960
|
+
* set that cannot be read at all returns before a single model call;
|
|
15774
15961
|
* - `keep-inbox`, an unknown key, a below-floor confidence, and an omitted member all leave the
|
|
15775
15962
|
* file where it is.
|
|
15776
15963
|
*
|
|
15964
|
+
* **Every refusal is its own count**, one key per class beside the `refused` total
|
|
15965
|
+
* ({@link PLACEMENT_REFUSALS}). Nine classes pooled into one number make a night where the phase
|
|
15966
|
+
* applied nothing unreadable without a log grep, which is exactly how issue #81's mechanism stayed
|
|
15967
|
+
* hidden for two runs.
|
|
15968
|
+
*
|
|
15777
15969
|
* **Inbound hrefs are rewritten in the same commit.** Integrity's dangling-href repair chases a
|
|
15778
15970
|
* target into the ARCHIVE by deriving `archivePathFor`; a placement move is not an archive, so this
|
|
15779
15971
|
* phase rewrites `<link>` elements in the files that point at each moved path itself, the same
|
|
@@ -15792,6 +15984,30 @@ const PLACEMENT_MEMBER_CHARS = 600;
|
|
|
15792
15984
|
* thirty new topics should earn them over several reviewed runs, not one.
|
|
15793
15985
|
*/
|
|
15794
15986
|
const PLACEMENT_NEW_DIR_CAP = 5;
|
|
15987
|
+
/**
|
|
15988
|
+
* Every refusal class, as its count key and the prose it logs. The keys ARE the partition of
|
|
15989
|
+
* `refused`, which stays the total.
|
|
15990
|
+
*
|
|
15991
|
+
* One map rather than a prose string per guard, so the count and the log line cannot name different
|
|
15992
|
+
* things and a class cannot exist without a count. `refused` keeps its meaning exactly — an existing
|
|
15993
|
+
* reader is unaffected — and every case in `deep.test.ts`'s guard g asserts the parts sum to it.
|
|
15994
|
+
*
|
|
15995
|
+
* `refusedTouched` and `refusedAlreadyMoved` are separate because they are separate facts: the first
|
|
15996
|
+
* is another phase's write, the second is THIS phase having already moved the file in an earlier row
|
|
15997
|
+
* of the same run. One key covering both would report a duplicate model answer as cross-phase
|
|
15998
|
+
* contamination, and an operator reading the count would go looking for a phase that wrote nothing.
|
|
15999
|
+
*/
|
|
16000
|
+
const PLACEMENT_REFUSALS = {
|
|
16001
|
+
refusedLowConfidence: "below the confidence floor",
|
|
16002
|
+
refusedDestination: "not a placeable directory",
|
|
16003
|
+
refusedTask: "tasks never move",
|
|
16004
|
+
refusedTouched: "a phase that decides wrote this file this run",
|
|
16005
|
+
refusedAlreadyMoved: "this run already moved this file",
|
|
16006
|
+
refusedNewDirCap: "the new-directory cap for this run is spent",
|
|
16007
|
+
refusedInvalidPath: "the destination path is not a valid memory path",
|
|
16008
|
+
refusedCollision: "the destination already holds a file by that name",
|
|
16009
|
+
refusedSourceGone: "the source file is already gone from the tree"
|
|
16010
|
+
};
|
|
15795
16011
|
const placementTriage = (env) => Effect.gen(function* () {
|
|
15796
16012
|
if (env.deep === void 0) return {
|
|
15797
16013
|
...emptyOutcome({ candidates: 0 }),
|
|
@@ -15828,12 +16044,12 @@ const placementTriage = (env) => Effect.gen(function* () {
|
|
|
15828
16044
|
PEOPLE_DIR
|
|
15829
16045
|
]);
|
|
15830
16046
|
const existingDirs = [...new Set(corpus.map((row) => row.path.slice(0, row.path.lastIndexOf("/"))).filter((dir) => (dir.startsWith("areas/") || dir.startsWith("resources/")) && !managed.has(dir)))].sort();
|
|
15831
|
-
/**
|
|
15832
|
-
|
|
15833
|
-
|
|
15834
|
-
|
|
15835
|
-
|
|
15836
|
-
|
|
16047
|
+
/**
|
|
16048
|
+
* Paths a phase whose write carries a decision already touched this run. A move of one would
|
|
16049
|
+
* fold that phase's edit into a rename a reviewer reads elsewhere; a SWEEP has no such edit.
|
|
16050
|
+
* Read BEFORE the batch loop, so a set that cannot be read costs no model call.
|
|
16051
|
+
*/
|
|
16052
|
+
const touched = yield* touchedThisRun(env.deps.git, env.baseSha);
|
|
15837
16053
|
const batches = assembleBatches([candidates], { maxMembers: 16 });
|
|
15838
16054
|
const counts = {
|
|
15839
16055
|
candidates: candidates.length,
|
|
@@ -15844,7 +16060,22 @@ const placementTriage = (env) => Effect.gen(function* () {
|
|
|
15844
16060
|
keptInbox: 0,
|
|
15845
16061
|
newDirs: 0,
|
|
15846
16062
|
budgetSkipped: 0,
|
|
15847
|
-
failed: 0
|
|
16063
|
+
failed: 0,
|
|
16064
|
+
existingDirs: existingDirs.length,
|
|
16065
|
+
touchedFiles: touched.kind === "unknown" ? 0 : touched.paths.size,
|
|
16066
|
+
touchedCommits: touched.kind === "scoped" ? touched.commits : 0,
|
|
16067
|
+
sweepCommits: touched.kind === "scoped" ? touched.sweeps : 0,
|
|
16068
|
+
touchedWidened: touched.kind === "widened" ? 1 : 0,
|
|
16069
|
+
...Object.fromEntries(Object.keys(PLACEMENT_REFUSALS).map((key) => [key, 0]))
|
|
16070
|
+
};
|
|
16071
|
+
/**
|
|
16072
|
+
* A touched set neither read could establish is a degradation, reported the way an absent model
|
|
16073
|
+
* is: a reason, nothing written, nothing committed. Moving under it would be moving without the
|
|
16074
|
+
* guard, and the corpus is still there tomorrow.
|
|
16075
|
+
*/
|
|
16076
|
+
if (touched.kind === "unknown") return {
|
|
16077
|
+
...emptyOutcome(counts),
|
|
16078
|
+
detail: "the run's touched set could not be read"
|
|
15848
16079
|
};
|
|
15849
16080
|
if (batches.length === 0 || env.dryRun) return emptyOutcome(counts);
|
|
15850
16081
|
const modelKey = modelFor(env.deps, "placement-triage");
|
|
@@ -15887,41 +16118,46 @@ const placementTriage = (env) => Effect.gen(function* () {
|
|
|
15887
16118
|
}
|
|
15888
16119
|
const refuse = (reason) => {
|
|
15889
16120
|
counts.refused = (counts.refused ?? 0) + 1;
|
|
15890
|
-
|
|
16121
|
+
counts[reason] = (counts[reason] ?? 0) + 1;
|
|
16122
|
+
return Effect.logWarning(`sleep.placement refused ${row.path} -> ${destination}: ${PLACEMENT_REFUSALS[reason]}`);
|
|
15891
16123
|
};
|
|
15892
16124
|
if (placement.confidence < .7) {
|
|
15893
|
-
yield* refuse("
|
|
16125
|
+
yield* refuse("refusedLowConfidence");
|
|
15894
16126
|
continue;
|
|
15895
16127
|
}
|
|
15896
16128
|
const bucket = paraBucketOf(destination);
|
|
15897
16129
|
if (bucket !== "areas" && bucket !== "resources" || managed.has(destination) || destination === row.path.slice(0, row.path.lastIndexOf("/")) || destination.split("/").some((segment) => segment === "" || segment === "." || segment === "..")) {
|
|
15898
|
-
yield* refuse("
|
|
16130
|
+
yield* refuse("refusedDestination");
|
|
15899
16131
|
continue;
|
|
15900
16132
|
}
|
|
15901
16133
|
if (isSleepExcluded(row.memory_type)) {
|
|
15902
|
-
yield* refuse("
|
|
16134
|
+
yield* refuse("refusedTask");
|
|
16135
|
+
continue;
|
|
16136
|
+
}
|
|
16137
|
+
if (touched.paths.has(row.path)) {
|
|
16138
|
+
yield* refuse("refusedTouched");
|
|
15903
16139
|
continue;
|
|
15904
16140
|
}
|
|
15905
|
-
if (
|
|
15906
|
-
yield* refuse("
|
|
16141
|
+
if (movedFrom.has(row.path)) {
|
|
16142
|
+
yield* refuse("refusedAlreadyMoved");
|
|
15907
16143
|
continue;
|
|
15908
16144
|
}
|
|
15909
16145
|
const isNew = !existingDirs.includes(destination) && !mintedDirs.has(destination);
|
|
15910
16146
|
if (isNew && mintedDirs.size >= 5) {
|
|
15911
|
-
yield* refuse("
|
|
16147
|
+
yield* refuse("refusedNewDirCap");
|
|
15912
16148
|
continue;
|
|
15913
16149
|
}
|
|
15914
16150
|
const target = `${destination}/${row.path.slice(row.path.lastIndexOf("/") + 1)}`;
|
|
15915
16151
|
if (!isValidMemoryPath(target)) {
|
|
15916
|
-
yield* refuse("
|
|
16152
|
+
yield* refuse("refusedInvalidPath");
|
|
15917
16153
|
continue;
|
|
15918
16154
|
}
|
|
15919
16155
|
if ((yield* readFileBytes(env, target)) !== void 0) {
|
|
15920
|
-
yield* refuse("
|
|
16156
|
+
yield* refuse("refusedCollision");
|
|
15921
16157
|
continue;
|
|
15922
16158
|
}
|
|
15923
16159
|
if ((yield* readFileBytes(env, row.path)) === void 0) {
|
|
15924
|
-
yield* refuse("
|
|
16160
|
+
yield* refuse("refusedSourceGone");
|
|
15925
16161
|
continue;
|
|
15926
16162
|
}
|
|
15927
16163
|
yield* attemptIo(`sleep.placement.mkdir:${target}`, async () => {
|
|
@@ -19440,4 +19676,4 @@ const latest = (left, right) => left === null ? right : right === null ? left :
|
|
|
19440
19676
|
|
|
19441
19677
|
//#endregion
|
|
19442
19678
|
export { expandRoot as $, EMBED_DIM as A, reinforce as B, discriminationGate as C, wrapAsData as D, ModelClientLive as E, readWatermark as F, makeGitPort as G, makeIndexer as H, Retrieval as I, makeDatabase as J, sanitizeFtsQuery as K, makeRetrieval as L, IndexRecorder as M, makeIndexRecorder as N, Embeddings as O, persistScanned as P, Store as Q, facetConditions as R, DiscriminationFailed as S, ModelClient as T, readIndexState as U, Indexer as V, IndexGit as W, STATE_MIGRATIONS_DIR as X, MIGRATIONS_DIR as Y, STATE_SCHEMA as Z, link as _, REINFORCE_SIGNALS as _t, parseSidecar as a, attemptIo as at, SLEEP_PHASES as b, archivedFormOf as c, Git as ct, accessRows as d, checkMemory as dt, makeStore as et, allPaths as f, parseMemory as ft, hrefFor as g, fenceOpeningOf as gt, applyHeadEdits as h, closesFence as ht, makeSleep as i, STATE_SIDECAR_PATH as it, EMBED_WATERMARK as j, EmbeddingsLive as k, generateArtifacts as l, makeGit as lt, publishRows as m, isValidDatetime as mt, scanTraceRoot as n, SLEEP_REPORTS_DIR as nt, renderSidecar as o, initRepo as ot, danglingEdges as p, setMeta as pt, DatabaseService as q, Sleep as r, STATE_DB_PATH as rt, reportFilename as s, readFileOrNull as st, mergeTailExtract as t, INDEX_DB_PATH as tt, DETECTION_PREFIX as u, commitSubject as ut, meta as v, frameKeyOf as vt, runDiscrimination as w, isSleepPhase as x, unlink as y, parseFacetFilters as z };
|
|
19443
|
-
//# sourceMappingURL=dist-
|
|
19679
|
+
//# sourceMappingURL=dist-xlKgJuWn.mjs.map
|