Commit graph

41 commits

Author SHA1 Message Date
1e5561e990 fix(actions): JSON kinds match --kind, and harden parsing and ranges
- JSON named kinds by their Rust names (public_expose, tender_offer,
  stock_dividend, reverse_split) while --kind and MCP take expose, tender,
  stock-dividend, reverse-split, so an output kind could not be passed
  back. JSON now uses the input names; old cache entries still read, and
  --kind also accepts underscores.
- {"dividend": null, "today": "..."} failed to parse and aborted the
  whole command; a null list beside scalars now reads as none.
- An extreme --from panicked on the default 30 days; it is an error now.
  The 366-day limit let 367 days through; it counts both ends now.
- An entitlement without a cum date sat on its ex date under a note that
  calls DATE the cum date. The table marks it (ex) and says what that
  means; JSON leaves cum_date out, as before, and the docs say so.
- A dividend's currency was ignored; one not in IDR now carries currency
  and the table shows it instead of Rp.
- The live check no longer fails a quiet month's empty calendar.

Claude-Session: https://claude.ai/code/session_01RqPPVeVDrnLoP7NUZmLRxY
2026-11-04 17:35:51 +07:00
73735767bb test(smoke): live cases for the Stockbit session (orderbook, actions, calendar)
Run only when `idx stockbit status` reports a session. They keep the
user's real config directory, which holds the session: an isolated copy
would refresh on its own and spend the user's rotating refresh token.

Claude-Session: https://claude.ai/code/session_01RqPPVeVDrnLoP7NUZmLRxY
2026-11-03 14:37:38 +07:00
hermes
74e5bb0536 feat: login-free Stockbit intraday and quote provider
Read Stockbit's public symbol page (__NEXT_DATA__) with a plain ureq
request and an honest user agent: `stocks intraday` (today by the
minute, any provider), `--provider stockbit` quotes with the best
bid/offer only while the market is open, and MCP `get_intraday`.

- UPSTREAMBLOCKED error (exit 4) for a CDN 403; never retried
- requests paced 1 s apart across idx processes, 5 s snapshot cache
- global --provider flag (yahoo, msn, stockbit)
- roadmap: L1 ships from Stockbit; depth stays a login follow-on
2026-10-23 04:58:32 +00:00
hermes
8b507d78e2 feat: foreign flow from IPOT by default (--source ipot|idx)
`idx stocks flow` now reads IPOT by default, which needs nothing installed:
per session, the all-investor broker summary gives the value traded and
the foreign one gives foreign buying (its footer), the foreign net and
selling (buying less the net), across all boards, through the broker
summary cache. IPOT's foreign net matched IDX's estimate within 0.3 B on
BBCA 2026-10-12 to 16.

`--source idx` (and `source` on get_foreign_flow) keeps IDX's trading
summary: exact share counts, close and the non-regular share, behind
curl-impersonate and Cloudflare.

- ForeignFlowDay gains foreign_buy_value, foreign_sell_value and
  foreign_share_bps for both sources (estimated at the session average
  for IDX); ForeignFlow gains source, the value totals and rounded.
- The table lays out each source's figures; the live smoke runs both.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V3syn2fN2LNo5pvTSqc6eh
2026-10-18 06:40:22 +00:00
hermes
29f143fa1b feat: foreign flow (idx stocks flow, get_foreign_flow)
`idx stocks flow SYMBOL` shows what foreign investors bought and sold of a
stock, session by session, from IDX's own daily trading summary: foreign
buy, sell and net volume, net value at the session's average price, and
the share of value traded outside the regular board, with range totals.
Latest 10 sessions by default (--days, at most 60) or --from/--to. The MCP
server exposes it as get_foreign_flow.

- api/idx: pure parser for GetStockSummary (rows dated another session
  are dropped) and a provider over curl-impersonate's profile fallback
  with a 30 s limit; Cloudflare passes only some fingerprints.
- flow: each session is cached whole by date, so other stocks' flows for
  the same sessions need no request. Weekends are never requested.
- Idx::idx_code is shared by broker and flow for the .JK-only check.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V3syn2fN2LNo5pvTSqc6eh
2026-10-18 05:39:12 +00:00
hermes
9f714dfed2 fix(publisher): require the jq and bash it uses; pin CI actions
From the Codex re-gate of the 0.3.3 fixes: NUL-separated discovery
fields need jq 1.7 (--raw-output0) and bash 4.4 (mapfile -d); check both
up front with a clear message instead of failing midway on older tools.
ci.yml actions are pinned to commit SHAs like the release workflows.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V3syn2fN2LNo5pvTSqc6eh
2026-10-18 02:16:18 +00:00
hermes
304709f3ac fix(release): robust publisher fields, WAL-safe copy, installer tag order, pinned actions
From the full-repo Codex audit:
- The publisher read discovery fields one per line, so a report title
  with newlines shifted every later field and published a manifest
  without source.publish_date, which every client rejects (blocking all
  syncs). Read them NUL-separated, and refuse to write a manifest missing
  a field clients require.
- The snapshot builder copied the SQLite file with cp; committed data
  still in the -wal file of an open database would be missing. Checkpoint
  first (a no-op for the publisher's closed database, so the bytes and
  hash are unchanged) and refuse to copy when that cannot complete.
- install.sh took the tag from a greedy match, which on a compact
  single-line releases response picks the last tag, not the newest (it
  installed v0.2.3 over v0.2.4 in the new test). Split fields first.
- scripts/audit-msn-fundamentals.sh read the symbol map from a path that
  no longer exists and silently checked no stocks.
- release.yml granted contents: write to every job; only publish gets it
  now. Actions in the release and snapshot workflows are pinned to commit
  SHAs.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V3syn2fN2LNo5pvTSqc6eh
2026-10-18 02:16:18 +00:00
hermes
346eb47cd4 fix(publisher): order releases by rowid, which every table has
id is the rowid alias in the real schema; rowid also works on the
minimal fixture DBs the publisher tests build.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V3syn2fN2LNo5pvTSqc6eh
2026-10-17 05:19:39 +00:00
hermes
6abc4a0712 fix(ownership): one release order everywhere; keep the old search API
From the Codex re-gate of the 0.3.3 fixes:
- `changes` broke ties between same-date releases by row id, but the
  ticker, entity, and graph queries and the snapshot builder did not, so
  two files imported in the same second could resolve differently per
  query. Order by as_of_date, imported_at, then id everywhere.
- search::fts_search and search::rebuild_fts were public in a published
  crate; keep them as deprecated wrappers.
- Invalid --source and --by are INVALIDINPUT, like other argument errors.
- The prefix-ranking test could not tell prefix from exact-alias ranking;
  add one where only prefix vs substring differs.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V3syn2fN2LNo5pvTSqc6eh
2026-10-17 05:07:41 +00:00
hermes
537b01901f test(npm): plant the macOS cache layout too
From the Codex re-gate: the planted-binary check used only ~/.cache, so
the old macOS fallback (~/Library/Caches) would have escaped it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V3syn2fN2LNo5pvTSqc6eh
2026-10-16 09:54:30 +00:00
hermes
d2cd7115f1 fix(npm): no cache fallback without an absolute home directory
The release-gate Codex review found the relative-path fix incomplete:
with XDG_CACHE_HOME unset and HOME empty or relative, the fallback was
`.cache/...` under the current directory, so a checkout could plant the
binary the launcher runs. Use the home cache only when the home path is
absolute; otherwise the package's bin/ is the only location. npm smoke
plants such a binary and checks it is never run (it was, before this).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V3syn2fN2LNo5pvTSqc6eh
2026-10-16 09:54:30 +00:00
hermes
3041606916 fix(npm): absolute cache root, per-arch cache dir, SIGINT during install
From a second Codex review (gpt-6.1-sol, xhigh):
- A relative XDG_CACHE_HOME (invalid per the XDG spec) made the launcher
  look for, and run, a binary under the current directory, which an
  untrusted checkout could supply. Use it only when absolute.
- The cache dir now includes <platform>-<arch>, so a home directory shared
  across machines cannot hand an arm64 host an x64 binary.
- SIGINT sent to the launcher's PID alone during the first-run install is
  forwarded to the installer (it was ignored); the launcher exits 130.
- npm smoke runs the first-run install with an empty user cache and checks
  the package binary was created, so a cached binary cannot pass for it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V3syn2fN2LNo5pvTSqc6eh
2026-10-16 09:54:30 +00:00
hermes
907223a838 fix(npm): install to a user cache when the package is read-only; signal-safe first run
- When the package's bin/ is not writable (a root-owned `sudo npm i -g`
  whose postinstall npm 12 skipped), install the binary to a per-user
  cache ($XDG_CACHE_HOME, ~/.cache, ~/Library/Caches on macOS, under
  <package>/<version>/); the launcher looks in both. Previously the first
  run failed with EACCES and advice to rerun npm install.
- Run the first-run installer asynchronously with SIGTERM/SIGHUP
  forwarded, so a supervisor stopping the launcher stops the download;
  the installer removes its partial file and the launcher exits by the
  same signal.
- Verify an IDX_BINARY_URL local file's checksum on the copy, not the
  source, so the installed bytes are the checked ones.
- npm smoke covers the read-only fallback.

The last three come from a Codex review.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V3syn2fN2LNo5pvTSqc6eh
2026-10-16 09:54:30 +00:00
hermes
bdbcf62a7b fix(npm): install the binary on first run when postinstall was skipped
npm 12 blocks install scripts of unapproved packages under `npx` and
`npm install -g` (its allow-scripts policy). The postinstall download never
ran, so `npx -y idx-mcp` exited with "installed binary not found" and MCP
clients saw only "Connection closed". Hermes Agent hits this by default: it
runs a bare `npx` with its own npm 12.

The launcher now runs the installer itself when the binary is missing,
logging to stderr so the idx-mcp stdout channel stays clean. This also covers
pnpm 10+ and bun, which skip dependency scripts by default.

npm-smoke installs idx-mcp with --ignore-scripts and checks that the first
run still starts cleanly.

Fixes #41

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WB13omP6h7YeyJx7GNtUnF
2026-10-16 04:40:40 +00:00
hermes
60634d3aef fix: address final review (path traversal, sync races, MCP limits)
Findings from a Codex (gpt-6.1-sol, xhigh) review of v0.2.4..HEAD, each
verified in the code before fixing:

- high: ticker symbols reached cache file paths unchecked, so a symbol
  like `../../REPORT` read (and deleted, if unparseable) .json files
  outside the cache, reachable through MCP get_quotes. resolve_symbol now
  accepts only ticker characters, and cache keys are encoded as single
  safe path components.
- high: sync decided before downloading and replaced the database
  without rechecking; an import that landed meanwhile was erased. It now
  rechecks the local state before installing, and idx-mcp runs
  ownership_sync exclusively with ownership reads.
- replaces_sqlite_sha256 never downgrades history: the client requires
  the rebuild to keep at least the local release count, and the
  publisher lists a replaced build only then.
- cache writes are atomic (temp file + rename), so concurrent readers
  never see, and delete, a partial entry.
- idx-mcp caps symbols (20, deduplicated) and limits, and runs at most 4
  tool calls at once.
- each Yahoo client gets its own cookie jar, paired with its crumb.
- ZIP entries (XLSX, KSEI archives) are read with a 256 MiB bound;
  non-ASCII archive dates and huge percentages are parse errors instead
  of panics or overflow.
- agent-smoke --keep deletes every copied agent config, not only auth
  files.

CLI goldens unchanged; the MCP tool golden changes only to state the
new caps.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 21:58:37 +00:00
hermes
8e3323e4f7 chore: release 0.3.0 with idx-mcp
Bump the workspace and npm packages to 0.3.0. The release workflow
builds and attaches idx-mcp binaries next to idx, the npm launcher and
installer pick their binary from package.json so npm/idx-mcp reuses
them, and the npm smoke test covers both packages.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 19:01:29 +00:00
hermes
ec17a1bd05 fix(ownership): update syncs when a same-date snapshot is rebuilt
A snapshot republished for the same as-of date (the TRUE ticker fix)
kept the date, latest release, and release count, so sync saw nothing
to do and existing installs kept the bad data without --force.

The manifest gains an optional snapshot.replaces_sqlite_sha256 listing
earlier builds for that date. Queries never write to a synced file, so
a local database byte-identical to a listed build is replaced; one that
matches nothing (local imports or edits) still needs --force. The
publisher fills the list on a same-date rebuild. Older clients ignore
the field.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 18:52:13 +00:00
hermes
fe627496dd test: add golden output parity gate and live A/B compare
Phase 0 of docs/DESIGN.md, before any refactor code:

- tests/golden.rs runs every case in tests/golden/cases.toml (126
  outputs across help, completions, config, cache, all stocks commands
  on MSN and Yahoo mocks, error paths, and ownership queries on a seeded
  fixture DB) and compares exit code, stdout, and stderr byte for byte.
  Regenerate only with UPDATE_GOLDEN=1 for a reviewed output change.
- scripts/live-smoke.sh --compare <old-idx> runs each live case with
  both binaries and fails on differing exit codes, added/missing/retyped
  JSON keys, or changed values for slow-moving data.
- Exclude golden files from the whitespace/EOF pre-commit fixers, which
  would otherwise rewrite pinned help output.

Both gates were fault-injected: a one-word table change fails exactly
the three history goldens, and a binary dropping profile.long_name fails
only the profile live comparison.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 08:33:25 +00:00
hermes
86c108f6eb test: replace mock smoke matrix with a live-only drift check
The 43-case mock smoke duplicated tests/cli.rs, which already runs the
real binary against fixture providers. Replace the flake's smoke-mock
check with a small packaged-cli check (version JSON, bash completions,
one mock quote), and cut scripts/live-smoke.sh from 672 to ~170 lines:
it now only runs shipped commands against live MSN/Yahoo/IDX and
validates output shape. Rewrite docs/SMOKE.md around the Nix gate and
the drift check.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 07:45:51 +00:00
hermes
26ffd445e8 ci(nix): unify cached verification and strengthen packaged smoke
Run deterministic installer and publisher checks in Nix, align dev and release dependency artifacts, narrow derivation inputs, and use content-aware CI cache keys. Replace duplicate Cargo gates with a Nix pre-push gate; validate JSON streams and cache data in representative packaged-binary smoke cases.
2026-10-03 16:22:11 +00:00
hermes
fea1a0e906 fix(ownership): bound snapshot download by manifest size; clean refused builds
Addresses review on #31:
- the snapshot download limit is the manifest's size_bytes (+1, since
  ureq fails once a body reaches its limit), capped at 1 GiB; a body
  larger than advertised fails during download. New CLI test; the
  >10 MiB test also caught the off-by-one.
- a refused (oversized) standalone build removes its sqlite and
  manifest from --output-dir.
- docs: downloads are buffered, not streamed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017a5Cv5Z1NzdYKgKnTiAF4E
2026-09-25 22:31:40 +00:00
hermes
de58d04da9 fix(ownership): sync snapshots over 10 MiB; refuse oversized publishes
On 2026-09-25 a snapshot built with --history 5 (15.3 MB) was
published, and `idx ownership sync` failed for every client:
"the response body is larger than request limit: 10485760" (ureq's
default body cap). It was republished by hand with --history 2 (8.2 MB).

- snapshot download: raise the ureq body limit to 1 GiB; the file is
  still verified against the manifest's size and SHA-256. The PDF/XLSX
  report downloads get a 256 MiB limit for the same reason.
- CLI test: sync a >10 MiB snapshot over HTTP (fails without the fix
  with the exact production error).
- build-latest-ownership-snapshot.sh: --max-snapshot-bytes (default
  10485760, the limit of released clients <= v0.2.3); an oversized
  snapshot fails the build, so the published one stays untouched.
- publish-ownership-snapshot.sh passes --max-snapshot-bytes through;
  publisher tests cover the failure and that the release is unchanged.
- docs: size table and guidance.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017a5Cv5Z1NzdYKgKnTiAF4E
2026-09-25 22:22:48 +00:00
hermes
9f4ed5b4a5 fix(ownership): verify snapshot assets before publishing manifests 2026-09-25 10:37:32 +00:00
hermes
6628d3b2e9 fix(ownership): make publisher RESULT contract hold on every path
Addresses review on #29:
- drop errtrace: a failure inside $(...) printed two RESULT lines
- explicit failures go through fail(), which always prints
  RESULT: FAILED stage=...; idx check gets its own `preflight` stage;
  the never-failing published-manifest read is no longer a stage
- validate the built latest_as_of_date (`// empty` + date regex) so a
  corrupt manifest cannot compare as "newer"
- freshness: validate --now (exit 2) and latest_as_of_date (exit 3)
  before calling date
- flake: package an explicit script list instead of scripts/*.sh
- docs: GH_TOKEN prerequisite for the packaged publisher

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017a5Cv5Z1NzdYKgKnTiAF4E
2026-09-24 21:50:51 +00:00
hermes
1f6f0ee02e feat(ownership): idempotent, packaged snapshot publisher
The self-hosted publish timer has not produced an upload since it was
enabled (manifest last updated 2026-07-14, a manual run), and failures
were invisible. Make the job safe to run daily and easy to deploy:

- publish-ownership-snapshot.sh: skip the upload when the published
  manifest already has the latest as-of date (pre-check via discover
  for XLSX sources, post-build check for legacy PDF); --force to
  override. Always ends with one line: RESULT: published|up-to-date
  <as-of> or RESULT: FAILED stage=<stage> (exit N). gh release create
  no longer requires a git checkout.
- check-ownership-snapshot-freshness.sh: exit 1 when the published
  latest_as_of_date is older than --max-age-days (default 40).
- flake: packages.ownership-publisher bundles the scripts with a
  pinned idx (curl-impersonate + mupdf) and jq/gh/sqlite/curl as
  bin/idx-ownership-publish and bin/idx-ownership-freshness, so hosts
  need no checkout, nix develop or cargo build at run time. Built in CI.
- contrib/systemd + docs: daily 06:00 UTC timer, run semantics table,
  freshness alarm, packaged usage.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017a5Cv5Z1NzdYKgKnTiAF4E
2026-09-24 21:40:02 +00:00
hermes
c866dd7a65 fix(ownership): harden --history parsing per review
Addresses review on #27: canonicalize --history ("08" no longer hits
bash octal parsing), run the history jq in a plain command
substitution so a jq failure aborts under set -e, and restore the
schedule-guidance paragraph in OWNERSHIP_SELF_HOSTED.md.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017a5Cv5Z1NzdYKgKnTiAF4E
2026-09-24 21:12:34 +00:00
hermes
c2d2734739 feat(ownership): build snapshots from XLSX with monthly history
- build-latest-ownership-snapshot.sh: discover with a wider limit and
  add --history <n> to import the n previous supported above-1% XLSX
  months (one per as-of date, oldest first) before the latest, so
  `idx ownership changes` works straight from a synced snapshot.
- publish-ownership-snapshot.sh: pass --history through (for the
  clan-private systemd timer via extraArgs).
- publish-ownership-snapshot.yml: `history` input (default 5).
- Docs: publish/self-hosted flow now describes the XLSX source,
  observed upload days (2nd-3rd of the month), and --history.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017a5Cv5Z1NzdYKgKnTiAF4E
2026-09-24 21:12:05 +00:00
hermes
aabb00d3b7 fix: expand quoted ~ in install dir and trim trailing slashes on mirror URLs
Addresses Pullfrog review on #24; adds a fixture test for both.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017a5Cv5Z1NzdYKgKnTiAF4E
2026-09-24 19:34:18 +00:00
hermes
a142a3718f feat: add curl | sh install script
- install.sh: POSIX sh installer that detects OS/arch (incl. Rosetta),
  resolves the newest vX.Y.Z release via the GitHub API (skipping
  ownership-snapshot-current), verifies SHA256SUMS, sanity-runs the
  binary, and installs atomically into ~/.local/bin without sudo.
  Supports --version/--dir and IDX_* env overrides; prints a PATH
  hint, a NixOS flake suggestion, and helper-tool install hints
  instead of calling package managers.
- scripts/install-sh-test.sh: fixture tests against a local fake
  release server (latest, pinned, piped, checksum mismatch, missing
  release, unsupported OS, PATH hint, reinstall).
- ci.yml: install-script job (ShellCheck, sh + dash on Linux, sh on
  macOS).
- release.yml: attach install.sh to each release.
- README + docs/INSTALL.md.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017a5Cv5Z1NzdYKgKnTiAF4E
2026-09-24 19:29:52 +00:00
hermes
4f8c90a108 fix: harden npm distribution release and installer
- release.yml: build static musl Linux binaries (no host glibc
  requirement), add darwin-x64, publish SHA256SUMS, allow
  workflow_dispatch for an existing tag (v0.2.3 has no binaries yet),
  and run build-only on PRs that touch the workflow or installer
- install.js: verify downloads against the release SHA256SUMS
  (IDX_BINARY_SHA256 for overrides), add a 60s request timeout, use
  stream.pipeline so truncated downloads fail, and skip the download
  inside the source checkout
- idx.js: forward SIGTERM/SIGHUP to the binary and keep the signal
  exit status
- package.json: drop the prepare script (no Rust toolchain needed to
  pack/publish)
- npm-smoke.sh: read the tarball name from npm pack, use grep over rg
- .gitignore: ignore /bin/ and *.tgz
- docs: musl targets, checksums, pnpm/bun opt-in, manual release flow

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017a5Cv5Z1NzdYKgKnTiAF4E
2026-09-24 18:57:53 +00:00
hermes
221dc01397 feat: add npm distribution wrapper for idx-cli
Add an npm wrapper (package.json + postinstall downloader) so the Rust
binary can be installed via npm/npx. Include a local smoke test, a GitHub
Release cross-compile workflow, and nodejs in the dev shell.

- package.json: idx-cli v0.2.3, bin idx, engines node>=16
- scripts/install.js: dependency-free postinstall that downloads the
  platform-specific GitHub Release asset (or IDX_BINARY_URL override)
- scripts/idx.js: binary launcher preserving exit codes/signals
- scripts/npm-smoke.sh: npm pack -> install tarball -> run idx --help
  and idx config --help, locale, no publish required
- .github/workflows/release.yml: cross-compile linux-x64/arm64 and
  darwin-arm64 releases and attach binaries to the version tag
- flake.nix: add nodejs_22 to the dev shell
- docs/NPM_DISTRIBUTION.md: usage, local test, and release flow
2026-08-18 11:14:55 +00:00
a2d15d2125 feat: support explicit MSN chart history 2026-04-15 13:48:27 +07:00
19748d397e fix: handle non-finite msn key ratios 2026-04-13 23:34:36 +07:00
411336e301 fix: refresh snapshot publish output dir 2026-04-13 18:21:36 +07:00
c67748d950 ops: draft self-hosted ownership snapshot publish flow 2026-04-13 16:46:53 +07:00
afa20d9b2f feat: add ownership snapshot publish flow 2026-04-12 13:35:09 +07:00
c94c826307 chore: prepare v0.2.1 release 2026-04-06 17:47:20 +07:00
8404b27628 Add ownership snapshot sync 2026-03-31 19:28:13 +07:00
a52712b063 Complete batch 2 ownership import hardening 2026-03-30 16:40:32 +07:00
7928e83cc5 test: reset smoke cache between warm cases
Clear the smoke cache before each cache-group warm case so the following offline and stale-cache checks always start from a fresh baseline instead of earlier group state.

Document the runner behavior in the smoke guide to make the cache-group sequencing explicit.
2026-03-30 12:35:22 +07:00
d23f16e7a3 test: live smoke testing 2026-03-27 16:53:57 +07:00