mirror of
https://github.com/PR0M3TH3AN/SeedPass.git
synced 2026-10-27 07:52:03 +00:00
Delete 38 assertions that were never necessary, and the bug one was hiding
I expected the 35 remaining findings to need a VaultIndex remodel. They did not. `vaultIndexSchema` and every entry schema are `.loose()`, which already gives both types an index signature — so `index["_system"]`, `entry["label"]` and assignment to `Record<string, unknown>` all compile with no cast at all. I checked before rewriting anything, and the answer was that 38 of the 44 were simply redundant, copied from each other rather than needed. That is the linter's real value here: it did not find a design flaw, it made me test an assumption I would otherwise have acted on. The remodel would have been work done to satisfy a belief that was false. Removing them let the compiler see three things the casts had been hiding: - `nextTotpIndex` filtered on `type === "totp" || kind === "totp"`, and TS reported the second half as unreachable. It is: withKind() populates kind from type at parse time and the union is discriminated on kind, so the fallback could never fire. Now filters on the discriminant alone. - `ensureIndex0Payload` always ran normalizeIndex0 and then returned `Dict`, throwing the guarantee away, so all three callers asserted it back with `as unknown as Index0`. It now returns an `Index0Payload` that carries what the function established, built in that shape rather than asserted into it, and the type propagates through compactIndex0Payload and rebuildCanonicalViewsPayload. - `sign()` in recoveryDrills declared a DrillRecord parameter it never used as one — it HMACs a canonical serialization — which forced the verifier to assert a shape it had not established while checking a signature. Also `rebuildIndex0Checkpoints` typed to the one field it reads, and console.ts asking `"isTTY" in this.out` rather than asserting a shape onto a WritableStream. One assertion remains, in entrySchema, and it is structural rather than evidential: z.preprocess types its output from an inner union deliberately wider than Entry, and no zod spelling expresses "parses to a superset, hands back the narrow type". Disabled inline with that written down. 44 -> 0. The lint step is now in ci_prepush.sh, and the gate runs green in 44.5 seconds. 689 tests still pass. Co-Authored-By: Claude Opus 5 (1M context) <[email protected]> Claude-Session: https://claude.ai/code/session_01Vtx94nXDmNgXEeThUZP4QN
This commit is contained in:
1 parent
e9d49d10bc
commit
72b3e52f50
11 files changed
+101
-58
No files matched your search
@@ -205,11 +205,11 @@ async function mutate<T>(
|
||||
try {
|
||||
const updated = emitEntryEvents(vault.index, {
|
||||
before,
|
||||
after: vault.index.entries as unknown as Record<string, unknown>,
|
||||
after: vault.index.entries,
|
||||
fingerprintDir: profileDir(ctx),
|
||||
now: Math.floor(ctx.now() / 1000),
|
||||
});
|
||||
(vault.index as unknown as Record<string, unknown>)["_system"] = updated["_system"];
|
||||
(vault.index)["_system"] = updated["_system"];
|
||||
} catch {
|
||||
// Derived state; never fail a committed write over it.
|
||||
}
|
||||
@@ -284,7 +284,7 @@ export function registerRoutes(server: ApiServer, ctx: ApiContext): void {
|
||||
const rows = Object.entries(vault.index.entries)
|
||||
.map(([id, entry]) => ({ id, entry: entry as Entry }))
|
||||
.filter(({ entry }) => {
|
||||
const e = entry as unknown as Record<string, unknown>;
|
||||
const e = entry;
|
||||
if (!includeArchived && e["archived"] === true) return false;
|
||||
if (kindFilter && String(e["kind"] ?? e["type"] ?? "") !== kindFilter) return false;
|
||||
if (!query) return true;
|
||||
@@ -631,9 +631,9 @@ export function registerRoutes(server: ApiServer, ctx: ApiContext): void {
|
||||
const vault = await readVault(ctx);
|
||||
const now = Math.floor(ctx.now() / 1000);
|
||||
const codes = Object.entries(vault.index.entries)
|
||||
.filter(([, e]) => String((e as unknown as Record<string, unknown>)["kind"] ?? "") === "totp")
|
||||
.filter(([, e]) => String((e)["kind"] ?? "") === "totp")
|
||||
.map(([id, e]) => {
|
||||
const entry = e as unknown as Record<string, unknown>;
|
||||
const entry = e;
|
||||
const secret = materializeSecret(vault.index, id, e as Entry, mnemonic, {
|
||||
timestamp: now,
|
||||
});
|
||||
@@ -660,9 +660,9 @@ export function registerRoutes(server: ApiServer, ctx: ApiContext): void {
|
||||
// Every TOTP secret in the vault, in plaintext. Password-gated for
|
||||
// that reason alone.
|
||||
const entries = Object.entries(vault.index.entries)
|
||||
.filter(([, e]) => String((e as unknown as Record<string, unknown>)["kind"] ?? "") === "totp")
|
||||
.filter(([, e]) => String((e)["kind"] ?? "") === "totp")
|
||||
.map(([, e]) => {
|
||||
const entry = e as unknown as Record<string, unknown>;
|
||||
const entry = e;
|
||||
// The SECRET, not a code. materializeSecret returns the current
|
||||
// 6-digit code for a totp entry, which would make this export look
|
||||
// correct and be useless — an authenticator cannot be seeded from
|
||||
@@ -894,7 +894,7 @@ export function registerRoutes(server: ApiServer, ctx: ApiContext): void {
|
||||
const counts: Record<string, number> = {};
|
||||
let archived = 0;
|
||||
for (const entry of Object.values(vault.index.entries)) {
|
||||
const e = entry as unknown as Record<string, unknown>;
|
||||
const e = entry;
|
||||
const kind = String(e["kind"] ?? e["type"] ?? "unknown");
|
||||
counts[kind] = (counts[kind] ?? 0) + 1;
|
||||
if (e["archived"] === true) archived++;
|
||||
|
||||
@@ -417,11 +417,11 @@ function recordIndex0(
|
||||
try {
|
||||
const updated = emitEntryEvents(vault.index, {
|
||||
before,
|
||||
after: vault.index.entries as unknown as Record<string, unknown>,
|
||||
after: vault.index.entries,
|
||||
fingerprintDir,
|
||||
now: Math.floor(Date.now() / 1000),
|
||||
});
|
||||
(vault.index as unknown as Record<string, unknown>)["_system"] = updated["_system"];
|
||||
(vault.index)["_system"] = updated["_system"];
|
||||
} catch {
|
||||
// Deliberately swallowed; see the note above.
|
||||
}
|
||||
@@ -476,7 +476,7 @@ async function openReadAccess(opts: GlobalOpts): Promise<ReadAccess> {
|
||||
* it needs the second factor to be live in the agent.
|
||||
*/
|
||||
const hydrate = async (id: string, entry: Entry): Promise<Entry> => {
|
||||
const record = entry as unknown as Record<string, unknown>;
|
||||
const record = entry;
|
||||
if (!isPartitionStub(record)) return entry;
|
||||
const client = new AgentClient(agentSocketPath(app.root));
|
||||
const fp = await currentFingerprint(app, opts);
|
||||
@@ -1478,10 +1478,7 @@ export function buildProgram(io: ProgramIo = defaultIo): Command {
|
||||
let local: Record<string, unknown> | null = null;
|
||||
if (localExists) {
|
||||
try {
|
||||
local = (await openVault(vaultPath, mnemonic)).index as unknown as Record<
|
||||
string,
|
||||
unknown
|
||||
>;
|
||||
local = (await openVault(vaultPath, mnemonic)).index;
|
||||
} catch {
|
||||
local = null; // unreadable local vault: treat the restore as recovery
|
||||
}
|
||||
@@ -2026,12 +2023,17 @@ export function buildProgram(io: ProgramIo = defaultIo): Command {
|
||||
await mutateVault(opts, async (vault) => {
|
||||
const partition = await readPartition(profileDir, tag);
|
||||
for (const [id, raw] of Object.entries(vault.index.entries)) {
|
||||
const entry = raw as unknown as Record<string, unknown>;
|
||||
const entry = raw;
|
||||
if (isPartitionStub(entry)) continue; // already moved
|
||||
const kind = String(entry["kind"] ?? entry["type"] ?? "").toLowerCase();
|
||||
if (!kinds.has(kind)) continue;
|
||||
partition[id] = entry;
|
||||
(vault.index.entries as unknown as Record<string, unknown>)[id] =
|
||||
// Same single assertion as entryOps.insert, for the same reason:
|
||||
// partitionStub builds a valid stub, but `kind` is a runtime string
|
||||
// and the entry union is keyed on literals, so the compiler cannot
|
||||
// see the two line up. Validated on the way back out by
|
||||
// parsePartitionRecord.
|
||||
(vault.index.entries as Record<string, Record<string, unknown>>)[id] =
|
||||
partitionStub(id, entry, kind, Date.now() / 1000);
|
||||
moved.push(id);
|
||||
}
|
||||
@@ -2487,7 +2489,7 @@ export function buildProgram(io: ProgramIo = defaultIo): Command {
|
||||
const byKind: Record<string, number> = {};
|
||||
let archived = 0;
|
||||
for (const entry of Object.values(vault.index.entries)) {
|
||||
const e = entry as unknown as Record<string, unknown>;
|
||||
const e = entry;
|
||||
const kind = String(e["kind"] ?? e["type"] ?? "unknown");
|
||||
byKind[kind] = (byKind[kind] ?? 0) + 1;
|
||||
if (e["archived"] === true) archived++;
|
||||
|
||||
@@ -73,7 +73,16 @@ function canonical(value: unknown): string {
|
||||
.join(",")}}`;
|
||||
}
|
||||
|
||||
function sign(key: Uint8Array, previousSig: string, record: DrillRecord): string {
|
||||
// Takes any record: it HMACs a canonical serialization, so the shape is not
|
||||
// something it needs or checks. Verification re-signs a parsed line and
|
||||
// compares — a wrong shape simply fails to match, which is reported as
|
||||
// sig_mismatch. Declaring DrillRecord here made the verifier assert a shape
|
||||
// it had not established, to satisfy a parameter that never used it.
|
||||
function sign(
|
||||
key: Uint8Array,
|
||||
previousSig: string,
|
||||
record: DrillRecord | Record<string, unknown>,
|
||||
): string {
|
||||
return hmacSha256Hex(key, utf8(`${previousSig}${canonical(record)}`));
|
||||
}
|
||||
|
||||
@@ -209,7 +218,7 @@ export async function verifyRecoveryDrills(
|
||||
continue;
|
||||
}
|
||||
delete body["sig"];
|
||||
if (sign(key, previousSig, body as unknown as DrillRecord) !== sig) {
|
||||
if (sign(key, previousSig, body) !== sig) {
|
||||
errors.push(`sig_mismatch_line:${index}`);
|
||||
}
|
||||
// Chain forward on the RECORDED signature, not the computed one: a single
|
||||
|
||||
@@ -53,7 +53,9 @@ export class ConsoleUi implements Ui {
|
||||
}
|
||||
|
||||
clear(): void {
|
||||
if ((this.out as unknown as { isTTY?: boolean }).isTTY) {
|
||||
// `in` narrows instead of asserting: NodeJS.WritableStream has no isTTY,
|
||||
// but the process streams do, and this asks rather than declares.
|
||||
if ("isTTY" in this.out && this.out.isTTY) {
|
||||
// Clear and reset the cursor, then clear scrollback: menus redraw
|
||||
// constantly, and without the third sequence a session leaves hundreds
|
||||
// of stale screens behind it.
|
||||
|
||||
@@ -123,7 +123,7 @@ function title(s: Session, breadcrumb: string): void {
|
||||
}
|
||||
|
||||
function kindOf(entry: Entry): string {
|
||||
const e = entry as unknown as Record<string, unknown>;
|
||||
const e = entry;
|
||||
return String(e["kind"] ?? e["type"] ?? "unknown");
|
||||
}
|
||||
|
||||
@@ -134,7 +134,7 @@ function kindOf(entry: Entry): string {
|
||||
* seed at the entry's index — the same secret the code is computed from.
|
||||
*/
|
||||
function totpUri(entry: Entry, mnemonic: string): string {
|
||||
const e = entry as unknown as Record<string, unknown>;
|
||||
const e = entry;
|
||||
const secret =
|
||||
typeof e["secret"] === "string" && e["secret"]
|
||||
? (e["secret"] as string)
|
||||
@@ -174,11 +174,11 @@ async function mutate(s: Session, fn: (vault: OpenedVault) => void): Promise<voi
|
||||
try {
|
||||
const updated = emitEntryEvents(fresh.index, {
|
||||
before,
|
||||
after: fresh.index.entries as unknown as Record<string, unknown>,
|
||||
after: fresh.index.entries,
|
||||
fingerprintDir: s.app.profileDir(s.fingerprint),
|
||||
now: Math.floor(s.clock() / 1000),
|
||||
});
|
||||
(fresh.index as unknown as Record<string, unknown>)["_system"] = updated["_system"];
|
||||
(fresh.index)["_system"] = updated["_system"];
|
||||
} catch {
|
||||
// Derived state; never fail a committed write over it.
|
||||
}
|
||||
@@ -1576,7 +1576,7 @@ async function exportTotpCodes(s: Session): Promise<void> {
|
||||
({ entry }) => kindOf(entry) === "totp" && !isArchived(entry),
|
||||
);
|
||||
const uris = rows.map(({ entry }) => ({
|
||||
label: String((entry as unknown as Record<string, unknown>)["label"] ?? ""),
|
||||
label: String((entry)["label"] ?? ""),
|
||||
uri: totpUri(entry, s.vault.mnemonic),
|
||||
}));
|
||||
// This file is every 2FA secret in the vault, in plaintext.
|
||||
|
||||
@@ -250,6 +250,18 @@ export const partitionStubSchema = z
|
||||
})
|
||||
.loose();
|
||||
|
||||
// The one assertion chain left in the codebase, and it is structural rather
|
||||
// than evidential. `z.preprocess` types its output from the inner union,
|
||||
// which here is `partitionStub | knownKinds | unknownKind` — a wider type
|
||||
// than `Entry` by construction, because a stub and an unknown-kind record are
|
||||
// both legitimate index members that no typed operation should touch. Every
|
||||
// consumer wants `Entry`. There is no zod spelling that expresses "parses to
|
||||
// a superset, hands back the narrow type" without an assertion.
|
||||
//
|
||||
// It is safe in the way the rule cares about: nothing is being CLAIMED about
|
||||
// unvalidated data. The value has just been parsed, one line above, by the
|
||||
// schema being cast.
|
||||
// oxlint-disable-next-line anti-slop/no-chained-type-assertions
|
||||
export const entrySchema = z.preprocess(
|
||||
withKind,
|
||||
// The stub comes first: it is the narrower shape, and a stub of a known
|
||||
|
||||
@@ -73,7 +73,7 @@ export function findDerivationCollisions(index: VaultIndex): DerivationCollision
|
||||
const byIndex = new Map<number, Array<{ id: string; kind: App32Kind; label: string }>>();
|
||||
|
||||
for (const [id, raw] of Object.entries(index.entries)) {
|
||||
const entry = raw as unknown as Record<string, unknown>;
|
||||
const entry = raw;
|
||||
const kind = String(entry["kind"] ?? entry["type"] ?? "");
|
||||
if (!(APP32_KINDS as readonly string[]).includes(kind)) continue;
|
||||
const derivationIndex = derivationIndexOf(id, entry);
|
||||
|
||||
@@ -96,7 +96,7 @@ const ALLOWED_FIELDS: Record<string, Set<string>> = {
|
||||
};
|
||||
|
||||
function entryDict(index: VaultIndex, id: string): Dict {
|
||||
const entry = (index.entries as unknown as Record<string, Dict>)[id];
|
||||
const entry = (index.entries)[id];
|
||||
if (typeof entry !== "object" || entry === null) {
|
||||
throw new Error(`Entry not found: ${id}`);
|
||||
}
|
||||
@@ -211,7 +211,7 @@ export interface ResolvedLink extends EntryLink {
|
||||
/** Links for an entry with resolved target metadata. */
|
||||
export function getLinks(index: VaultIndex, id: string): ResolvedLink[] {
|
||||
const src = entryDict(index, id);
|
||||
const entries = index.entries as unknown as Record<string, Dict>;
|
||||
const entries = index.entries;
|
||||
return normalizeLinks(src["links"] ?? []).map((link) => {
|
||||
const target = entries[String(link.target_id)];
|
||||
return {
|
||||
|
||||
@@ -75,7 +75,7 @@ export function nextIndex(index: VaultIndex): number {
|
||||
|
||||
/** The persisted allocation watermark, 0 when absent (pre-watermark vault). */
|
||||
function storedNextIndex(index: VaultIndex): number {
|
||||
const meta = (index as unknown as Dict)["_sync_meta"];
|
||||
const meta = (index)["_sync_meta"];
|
||||
if (typeof meta !== "object" || meta === null || Array.isArray(meta)) return 0;
|
||||
const raw = Number((meta as Dict)["next_index"]);
|
||||
return Number.isSafeInteger(raw) && raw > 0 ? raw : 0;
|
||||
@@ -83,7 +83,7 @@ function storedNextIndex(index: VaultIndex): number {
|
||||
|
||||
/** One past the highest tombstoned id. Non-numeric tombstone keys are historical junk and skipped. */
|
||||
function tombstoneFloor(index: VaultIndex): number {
|
||||
const meta = (index as unknown as Dict)["_sync_meta"];
|
||||
const meta = (index)["_sync_meta"];
|
||||
if (typeof meta !== "object" || meta === null || Array.isArray(meta)) return 0;
|
||||
const tombstones = (meta as Dict)["tombstones"];
|
||||
if (typeof tombstones !== "object" || tombstones === null || Array.isArray(tombstones)) return 0;
|
||||
@@ -103,7 +103,7 @@ function tombstoneFloor(index: VaultIndex): number {
|
||||
* preserve it without a schema bump.
|
||||
*/
|
||||
function bumpNextIndex(index: VaultIndex, id: number): void {
|
||||
const container = index as unknown as Dict;
|
||||
const container = index;
|
||||
const meta =
|
||||
typeof container["_sync_meta"] === "object" &&
|
||||
container["_sync_meta"] !== null &&
|
||||
@@ -116,8 +116,12 @@ function bumpNextIndex(index: VaultIndex, id: number): void {
|
||||
|
||||
/** Next TOTP derivation index: max over totp entries' index field + 1. */
|
||||
export function nextTotpIndex(index: VaultIndex): number {
|
||||
const indices = Object.values(index.entries as unknown as Record<string, Dict>)
|
||||
.filter((e) => e["type"] === "totp" || e["kind"] === "totp")
|
||||
const indices = Object.values(index.entries)
|
||||
// `kind` alone, not `type || kind`. withKind() populates kind from type
|
||||
// during parsing and the entry union is discriminated on it, so the
|
||||
// second half of that condition could never be true — the compiler said
|
||||
// so the moment the casts stopped blinding it.
|
||||
.filter((e) => e["kind"] === "totp")
|
||||
.map((e) => Math.trunc(Number(e["index"] ?? 0)));
|
||||
return indices.length > 0 ? Math.max(...indices) + 1 : 0;
|
||||
}
|
||||
@@ -133,7 +137,10 @@ function insert(index: VaultIndex, id: number, entry: Dict): string {
|
||||
if (Object.prototype.hasOwnProperty.call(index.entries, key)) {
|
||||
throw new Error(`refusing to overwrite existing entry ${key}`);
|
||||
}
|
||||
(index.entries as unknown as Dict)[key] = entry;
|
||||
// One direct assertion, not a chain: `insert` receives an entry the caller
|
||||
// has just assembled field by field, and a literal's `type: "totp"` widens
|
||||
// to string without a const assertion at every call site.
|
||||
(index.entries as Record<string, Dict>)[key] = entry;
|
||||
// Covers explicit-id inserts too: an entry created at #500 pushes the
|
||||
// watermark past 500 even though nextIndex never saw it.
|
||||
bumpNextIndex(index, id);
|
||||
|
||||
@@ -444,13 +444,27 @@ export function deriveIndex0Context(
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* A payload whose `_system.index0` has been through normalizeIndex0.
|
||||
*
|
||||
* The type exists so the guarantee survives the return. ensureIndex0Payload
|
||||
* always normalized, but declared `Dict`, so all three callers immediately
|
||||
* asserted the knowledge back with `as unknown as Index0` — three chained
|
||||
* assertions standing in for a fact the function had already established one
|
||||
* line earlier.
|
||||
*/
|
||||
export type Index0Payload = Dict & { _system: Dict & { index0: Index0 } };
|
||||
|
||||
/** Ensure `_system.index0` exists and is normalized, without touching entries. */
|
||||
export function ensureIndex0Payload(data: unknown): Dict {
|
||||
const out = isDict(data) ? { ...data } : {};
|
||||
const system = isDict(out["_system"]) ? { ...(out["_system"] as Dict) } : {};
|
||||
system["index0"] = normalizeIndex0(system["index0"]);
|
||||
out["_system"] = system;
|
||||
return out;
|
||||
export function ensureIndex0Payload(data: unknown): Index0Payload {
|
||||
const base = isDict(data) ? { ...data } : {};
|
||||
const system = isDict(base["_system"]) ? { ...(base["_system"] as Dict) } : {};
|
||||
// Built in the shape it promises rather than assembled and then asserted,
|
||||
// so nothing here has to be taken on trust.
|
||||
return {
|
||||
...base,
|
||||
_system: { ...system, index0: normalizeIndex0(system["index0"]) },
|
||||
};
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------- events
|
||||
@@ -528,7 +542,7 @@ export interface AppendEventOptions extends Omit<
|
||||
*/
|
||||
export function appendIndex0Event(payload: unknown, options: AppendEventOptions): Dict {
|
||||
const out = ensureIndex0Payload(payload);
|
||||
const system = (out["_system"] as Dict)["index0"] as unknown as Index0;
|
||||
const system = out._system.index0;
|
||||
const context = deriveIndex0Context(options.fingerprintDir, {
|
||||
...(options.actorType !== undefined && { actorType: options.actorType }),
|
||||
});
|
||||
@@ -609,7 +623,10 @@ export function buildDailyCheckpoint(
|
||||
}
|
||||
|
||||
export function rebuildIndex0Checkpoints(
|
||||
index0: Dict,
|
||||
// Reads only `events`, like recomputeIndex0Stats reads only three fields.
|
||||
// Typed to that rather than to a bare Dict, so an Index0 goes in without a
|
||||
// cast.
|
||||
index0: Pick<Index0, "events">,
|
||||
options: { maxCheckpointsPerWriter?: number } = {},
|
||||
): Record<string, Dict> {
|
||||
const maxPerWriter = options.maxCheckpointsPerWriter ?? INDEX0_MAX_CHECKPOINTS_PER_WRITER;
|
||||
@@ -661,7 +678,7 @@ export function compactIndex0(
|
||||
): Index0 {
|
||||
const normalized = normalizeIndex0(index0);
|
||||
normalized.checkpoints = rebuildIndex0Checkpoints(
|
||||
normalized as unknown as Dict,
|
||||
normalized,
|
||||
options,
|
||||
);
|
||||
normalized.stats = recomputeIndex0Stats(normalized);
|
||||
@@ -789,9 +806,9 @@ function buildRecentActivityView(
|
||||
export function rebuildCanonicalViewsPayload(
|
||||
payload: unknown,
|
||||
options: { fingerprintDir?: string | null } = {},
|
||||
): Dict {
|
||||
): Index0Payload {
|
||||
const out = ensureIndex0Payload(payload);
|
||||
const index0 = (out["_system"] as Dict)["index0"] as unknown as Index0;
|
||||
const index0 = out._system.index0;
|
||||
const entries = normalizeMapping(out["entries"]);
|
||||
const events = Object.values(index0.events)
|
||||
.map((event) => normalizeIndex0Event(event))
|
||||
@@ -864,9 +881,9 @@ export function rebuildCanonicalViewsPayload(
|
||||
export function compactIndex0Payload(
|
||||
payload: unknown,
|
||||
options: { maxCheckpointsPerWriter?: number; fingerprintDir?: string | null } = {},
|
||||
): Dict {
|
||||
): Index0Payload {
|
||||
const out = ensureIndex0Payload(payload);
|
||||
(out["_system"] as Dict)["index0"] = compactIndex0((out["_system"] as Dict)["index0"], {
|
||||
out._system.index0 = compactIndex0(out._system.index0, {
|
||||
...(options.maxCheckpointsPerWriter !== undefined && {
|
||||
maxCheckpointsPerWriter: options.maxCheckpointsPerWriter,
|
||||
}),
|
||||
@@ -885,7 +902,7 @@ export function buildManifestIndex0Metadata(
|
||||
const compacted = compactIndex0Payload(payload, {
|
||||
...(options.fingerprintDir !== undefined && { fingerprintDir: options.fingerprintDir }),
|
||||
});
|
||||
const index0 = (compacted["_system"] as Dict)["index0"] as unknown as Index0;
|
||||
const index0 = compacted._system.index0;
|
||||
const checkpoints = Object.values(index0.checkpoints).filter(isDict);
|
||||
const selected = [...checkpoints]
|
||||
.sort((a, b) => {
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
# preflight 2.6s
|
||||
# poetry check --lock 0.6s
|
||||
# black --check 2.5s
|
||||
# oxlint 0.9s (not enabled yet, see below)
|
||||
# oxlint 0.9s
|
||||
# tsc --noEmit (workspace) 7.3s
|
||||
# pytest determinism subset 5.4s
|
||||
# vitest core (node) 24.0s
|
||||
@@ -50,14 +50,8 @@ poetry check --lock
|
||||
step "python: formatting (the pinned black, not whatever is on PATH)"
|
||||
poetry run black --check .
|
||||
|
||||
# NOT YET. `pnpm -C js lint` currently exits non-zero on 35 known chained type
|
||||
# assertions -- one design problem (VaultIndex does not model what a vault
|
||||
# index contains) counted 35 times. Putting a red check in the push gate is
|
||||
# how a gate becomes something people route around, so it goes in when it goes
|
||||
# green, not before. Track it with: pnpm -C js lint
|
||||
#
|
||||
# step "javascript: lint"
|
||||
# pnpm -C js lint
|
||||
step "javascript: lint"
|
||||
pnpm -C js lint
|
||||
|
||||
step "javascript: typecheck"
|
||||
pnpm -C js typecheck
|
||||
|
||||
Reference in new issue
Block a user