532f61d closed the traversal half of this input. It did not close the mislabeling half, which is
the failure advArchiveFolder's own JSDoc says the per-snapshot folder exists to prevent:
a single folder holds the cumulative archive and every month at once — and an ingest reading it
stamps thirteen years of filings with the period it was asked for.
Both inputs below pass every guard the commit added. "2026-06" is a valid period, so
advArchiveFolder(input.snapshot) is satisfied (IngestAdvSnapshotTask.ts:186); adv/cumulative
resolves inside SEC_RAW_DATA_FOLDER, so the prefix check is satisfied (:188-194). Nothing
checks that the folder is this snapshot's folder.
Reproduction (passes today)
Against TestingDI, with a real 2026-06 archive and a real cumulative archive on disk:
await new IngestAdvSnapshotTask().run({ snapshot: "2026-06" });
expect((await advisers.query({ snapshot: "2026-06" })) ?? []).toHaveLength(1); // the real June row
await new IngestAdvSnapshotTask().run({
snapshot: "2026-06",
folder: advArchiveFolder("2011-2024"), // both guards satisfied
});
const landed = (await advisers.query({ snapshot: "2026-06" })) ?? [];
expect(landed).toHaveLength(1); // PASSES
expect(landed[0].crd_number).toBe("330001"); // PASSES — the cumulative archive's adviser
expect(landed[0].date_submitted).toBe("2013-04-01"); // PASSES — stamped snapshot "2026-06"
The real June-2026 row is destroyed by the deleteSearch that runs first (:210-213), and a 2013
filing takes its place under the June-2026 label. sec show advisers --min-aum then filters on a
2013 AUM, and sec status's newest-snapshot reading reports 2013 data as June 2026 — the exact
consequence the commit message describes fixing, one level up from where it was fixed.
folder has no caller
$ grep -rn "IngestAdvSnapshotTask" src --include=*.ts | grep -v '\.test\.'
src/cli/sync/registerSecSyncLeaves.ts:209: new IngestAdvSnapshotTask({
src/cli/groups/bootstrap.ts:289: new IngestAdvSnapshotTask({
$ grep -rn "folder:" src/cli src/task/adv --include=*.ts | grep -v '\.test\.'
src/task/adv/IngestAdvSnapshotTask.ts:166: folder: Type.Optional(Type.String()),
Both call sites pass only snapshot. The input exists solely as surface for sec-base task run,
the web console form and the MCP tool surface — which is precisely the threat model the commit
message names.
Fix
Delete the input, or set const dir = resolve(root, advArchiveFolder(input.snapshot)) and drop
folder from the schema. Either makes the label and the bytes one decision again, and removes the
prefix check along with the input it was written for. If it must stay for a manual re-extract,
require it to equal advArchiveFolder(snapshot) and say so in the error.
Two smaller notes on the same guard:
- The check is
resolve()-based, so it does not resolve symlinks. A symlink under
SEC_RAW_DATA_FOLDER passes.
input.folder ?? snapshotFolder does not catch "", so folder: "" resolves to the raw-data
root itself and throws rather than defaulting — a confusing message for an empty string.
Also: 0.2.0 on npm predates this fix
@workglow/sec@0.2.0 was published 2026-09-08T22:27Z; 532f61d landed 2026-09-09 19:31. The
published build still joins SEC_RAW_DATA_FOLDER with a completely unvalidated folder and skips
advArchiveFolder's check on snapshot when one is supplied. See the release issue.
Found during the 2026-09-14 review. Snapshot: workglow-dev/prd → analysis/grades/2026-09-14/sec-detailed.md §4.1.
532f61dclosed the traversal half of this input. It did not close the mislabeling half, which isthe failure
advArchiveFolder's own JSDoc says the per-snapshot folder exists to prevent:Both inputs below pass every guard the commit added.
"2026-06"is a valid period, soadvArchiveFolder(input.snapshot)is satisfied (IngestAdvSnapshotTask.ts:186);adv/cumulativeresolves inside
SEC_RAW_DATA_FOLDER, so the prefix check is satisfied (:188-194). Nothingchecks that the folder is this snapshot's folder.
Reproduction (passes today)
Against
TestingDI, with a real 2026-06 archive and a real cumulative archive on disk:The real June-2026 row is destroyed by the
deleteSearchthat runs first (:210-213), and a 2013filing takes its place under the June-2026 label.
sec show advisers --min-aumthen filters on a2013 AUM, and
sec status's newest-snapshot reading reports 2013 data as June 2026 — the exactconsequence the commit message describes fixing, one level up from where it was fixed.
folderhas no callerBoth call sites pass only
snapshot. The input exists solely as surface forsec-base task run,the web console form and the MCP tool surface — which is precisely the threat model the commit
message names.
Fix
Delete the input, or set
const dir = resolve(root, advArchiveFolder(input.snapshot))and dropfolderfrom the schema. Either makes the label and the bytes one decision again, and removes theprefix check along with the input it was written for. If it must stay for a manual re-extract,
require it to equal
advArchiveFolder(snapshot)and say so in the error.Two smaller notes on the same guard:
resolve()-based, so it does not resolve symlinks. A symlink underSEC_RAW_DATA_FOLDERpasses.input.folder ?? snapshotFolderdoes not catch"", sofolder: ""resolves to the raw-dataroot itself and throws rather than defaulting — a confusing message for an empty string.
Also:
0.2.0on npm predates this fix@workglow/sec@0.2.0was published2026-09-08T22:27Z;532f61dlanded2026-09-09 19:31. Thepublished build still joins
SEC_RAW_DATA_FOLDERwith a completely unvalidatedfolderand skipsadvArchiveFolder's check onsnapshotwhen one is supplied. See the release issue.Found during the 2026-09-14 review. Snapshot:
workglow-dev/prd→analysis/grades/2026-09-14/sec-detailed.md§4.1.