Task: Full-repository improvement audit at 24947bc — verified P1/P2/P3 remediation checklist

Open
#565 2 comments 1 reaction 1 assignee View on GitHub

Maintainers usually reply within 1 day

@Kuldeep2822k is already working on this.

Since Oct 31, 2026.

  • #587 by @Kuldeep2822k — open
  • #588 by @Kuldeep2822k — open
  • #589 by @Kuldeep2822k — open
  • #590 by @Kuldeep2822k — open

Assessment

This issue has not been assessed yet.

Description

chore ci cli core dependencies P1 security storage tests working

Description

A full-repository, read-only audit across correctness, security, performance, tests, architecture, dependencies, DX, docs, and direction. Every finding below was re-opened and confirmed in source at 24947bc; line references are against that commit. Baseline at audit time: npm run typecheck exit 0, npm run lint exit 0, npm audit reports 0 vulnerabilities.

The checklist is ordered by leverage (impact ÷ effort, discounted by confidence and fix risk). Each item is independently shippable. Done means the fix plus a regression test that fails when the deciding predicate is reverted, with the verification loop green:

npm run typecheck
npm run lint
npm test

Behavior changes also update the affected ADR or invariant (docs/adr/, planning/invariants.md, agent.md).

Scope of this issue: 12 P1, 12 P2, and 18 P3 findings, plus four direction options. Security-sensitive details are included at the maintainer's explicit request after the public-visibility warning; no secret values appear anywhere in this report.

Motivation

The repository is mature and heavily tested, but the audit found a small number of data-corruption and lost-update paths in the write-critical core (SM-2 scheduling, adoption, undo, session memory), one documented network-boundary bypass, one release-workflow injection class, and a set of smaller contract/test/perf gaps. Fixing the P1 set protects user notes and the published artifact; the P2/P3 set removes recurring classes of drift rather than individual symptoms.

Acceptance criteria

Definition of done (applies to every checked item)
  • The fix is implemented in the files named by the item; no out-of-scope behavior changes.
  • A regression test exists that fails when the deciding predicate is removed or reverted.
  • npm run typecheck, npm run lint, and npm test pass.
  • Exit-code and --json contracts are unchanged unless the item explicitly says otherwise.
  • If the item changes documented behavior, the relevant ADR/invariant/README text moves with it.
P1 — data safety, contract, and boundary fixes
  • P1-01 — Cap SM-2 intervals so due_at cannot overflow the JavaScript Date range.

    • Evidence: src/engine/sm2.ts:171 multiplies interval_days * ease_factor; :190 only rejects a non-finite result, not a date-range overflow; :264 advances with due.setDate(...); src/application/record-review.ts:90-112 formats the result into due_at.
    • Reproduced against dist: review 15 (quality 5) writes interval_days: 30216170 and due_at: 84755-11-24; review 16 writes 120864680 and NaN-NaN-NaN, with exit 0 and no error.
    • Impact: palee review silently persists an invalid date; valid-review-dates then reports a manual-only error, and the invalid day disables early-review detection permanently.
    • Fix: after the finite check, clamp to MAX_INTERVAL_DAYS = 36500 (100 years, the common SRS ceiling); guard computeDueDate against a non-finite due.getTime() as defense in depth; amend ADR-0001 and INV-17 to record the cap. The existing 1e308 non-finite throw test must keep passing.
    • Tests: 15/16/17 consecutive quality-5 reviews produce a valid future YYYY-MM-DD within the cap; computeDueDate refuses an invalid result; existing SM-2 tests unchanged.
    • Effort/Risk/Confidence: S / LOW / HIGH.
  • P1-02 — Re-check a batch-adoption target's palee_id in the preflight re-read.

    • Evidence: the scan stages an unadopted note at src/cli/adopt.ts:599-612; phase 1 re-reads fresh bytes and parses frontmatter at :1374-1378 but ignores frontmatter.palee_id; the write at :1440-1456 mints a fresh ID and uses the fresh fingerprint, so OCC passes.
    • Impact: two overlapping adopt --all runs (or a pause at the confirmation prompt) can make the loser overwrite the winner's palee_id and reset its review state; dependency edges naming the first ID dangle.
    • Fix: after the fresh parse, if freshFrontmatter.palee_id is a valid non-empty string (or differs from the planned note.topicId), skip the note with an explicit conflict/skip reason; never write a replacement identity. Preserve the existing single-run behavior.
    • Tests: simulate the scan → preflight → write sequence with the note becoming adopted between scan and preflight; assert no second ID is written and the reason names the conflict.
    • Effort/Risk/Confidence: S / LOW / HIGH.
  • P1-03 — Pass the written fingerprint through undo rollback.

    • Evidence: RollbackRecord in src/cli/adopt-undo.ts:134-138 carries no fingerprint; rollbackBatch at :383-394 calls three-argument atomicWrite, disabling OCC; adoption's sibling rollback was fixed for exactly this (#334) at src/cli/adopt.ts:1457-1462.
    • Impact: after a mid-batch undo failure, rollback blindly rewrites originals and destroys any edit made since the failed pass, while reporting restored.
    • Fix: journal computeFingerprint(item.updatedContent) as writtenFingerprint; call atomicWrite(vault, path, originalContent, writtenFingerprint, { expectExists: true }); report a per-note conflict instead of clobbering.
    • Tests: edit a journaled note after the failed pass, run rollback, assert the edit survives and the conflict is reported.
    • Effort/Risk/Confidence: S / LOW / HIGH.
  • P1-04 — Apply the schema gate to hot.md's newest-session selection.

    • Evidence: regenerateIndex skips sessions whose palee_schema !== SUPPORTED_SCHEMA_VERSION at src/storage/memory.ts:625-636; rebuildHotAndIndex selects the newest session at :749-765 with no schema check. ADR-0019:41-43 states both derived views share one eligibility predicate.
    • Impact: last_session and the hot-memory body can name a schema-2/unlabeled session that index.md excludes; the derived views disagree and validation sees a phantom reference.
    • Fix: add the same frontmatter.palee_schema === SUPPORTED_SCHEMA_VERSION predicate to the hot selection; keep draft/zero-byte behavior unchanged.
    • Tests: a newer schema-2 session plus an older schema-1 session; assert index.md and hot.md both select the schema-1 session.
    • Effort/Risk/Confidence: S / LOW / HIGH.
  • P1-05 — Normalize a trailing root-label dot before the .internal refusal.

    • Evidence: src/ai/provider.ts:337-352 tests host.endsWith('.internal'); :368-383 passes URL.hostname straight in. new URL('https://metadata.google.internal./').hostname keeps the trailing dot, so the suffix test is false. ADR-0027:40 promises the .internal suffix is refused.
    • Impact: the named metadata-host refusal is bypassed; an HTTPS probe of metadata.google.internal. is permitted, violating the documented "named, not just numbered" boundary.
    • Fix: strip one trailing root-label dot from URL.hostname before all shape/suffix checks; add metadata.google.internal. and mixed-case .INTERNAL. cases; confirm IPv6 and ordinary hostnames are unchanged.
    • Tests: the normalized endpoint is refused with the reserved-address config error; https://api.internal-host.example remains allowed.
    • Effort/Risk/Confidence: S / LOW / HIGH.
  • P1-06 — Stop interpolating manifest-derived values into release shell steps.

    • Evidence: .github/workflows/release.yml:57-68 writes filename/version from package.json to step outputs; :161 uses VERSION=${{ needs.verify-and-pack.outputs.version }} and :171 runs npm publish ${{ needs.verify-and-pack.outputs.package-filename }} in the step holding NPM_TOKEN and id-token: write; .github/workflows/ci.yml:181,186 repeats the pattern; scripts/verify-tarball.js:4 builds tar -tzf ${filename} from the same manifest.
    • Impact: a version or filename containing shell metacharacters is parsed by the shell before npm starts, executing on the release runner with registry and OIDC credentials. Triggering requires write/tag/release access; this is a supply-chain hardening issue, not an unauthenticated attack.
    • Fix: validate name/version against the npm package-name and semver rules before use; pass values through env: and quote them in run:; use execFileSync with an argv array in verify-tarball.js; keep the existing tarball assertions.
    • Tests: add a script-level validation test or a CI lint check that rejects a non-semver version; confirm the release workflow still packs, verifies, and publishes.
    • Effort/Risk/Confidence: S / LOW / HIGH.
  • P1-07 — Fold lock keys on case-insensitive platforms.

    • Evidence: src/storage/lock.ts:173-192 hashes path.relative(realpath(vault), realpath(target)) with case preserved; Node's realpathSync preserves the caller's spelling on Windows (verified: D:/CLI-BUILD → D:\CLI-BUILD), while src/storage/containment.ts:19-24 deliberately compares case-insensitively.
    • Impact: two processes spelling the same note Notes/foo.md and notes/foo.md take different lock directories, defeating mutual exclusion; creation and lock-only unlink paths can race.
    • Fix: fold the vault-relative lock key with foldIdentity (lowercase + NFC) on win32 and darwin before hashing; keep Linux case-sensitive. Existing lock directories are ephemeral.
    • Tests: on win32, acquire a lock with a case-varied spelling and assert the second spelling conflicts; on Linux, assert distinct-case paths remain distinct.
    • Effort/Risk/Confidence: S / LOW-MED / HIGH.
  • P1-08 — State non-existence on roadmap first-creation writes.

    • Evidence: src/cli/roadmap.ts:908-931 inspects with a single lstat and builds synthetic content for isNew; :962 calls atomicWrite(vault, path, content, fingerprint) with a null fingerprint and no expectExists, so no guard runs. AtomicWriteOptions at src/storage/atomic-write.ts:200-223 exists for this exact case.
    • Impact: a file created between the lstat and the rename is silently overwritten — the #327 lost-update class in a .palee-writer path.
    • Fix: pass { expectExists: false } when isNew; treat the resulting conflict as a retryable per-topic failure.
    • Tests: create the target after inspection and before the write; assert the original bytes survive and the conflict is reported.
    • Effort/Risk/Confidence: S / LOW / HIGH.
  • P1-09 — Stop matching slash-containing non-wildcard patterns at any depth.

    • Evidence: src/storage/pattern-matcher.ts:337-355 applies endsWith('/'+pattern) and includes('/'+pattern+'/') to non-wildcard patterns even when the pattern contains /; docs/02-1-topic-management-commands.md:133 says a / pattern is anchored at the vault root and must name the whole path. Directory-prefix matching (notes/café selecting its subtree) is deliberate and pinned by test/storage-pattern-matcher.test.ts:129-137.
    • Impact: a/x.md matches b/a/x.md, so --include/--exclude select notes outside the named path; the filter reports work it was not asked to do.
    • Fix: for patterns containing /, keep exact match and startsWith(pattern + '/') (directory-prefix semantics), and drop only the endsWith/includes arms. Non-slash patterns keep basename matching. Update the dialect comment if the directory-prefix rule needs spelling out.
    • Tests: matchesPattern('b/a/x.md', 'a/x.md') === false; matchesPattern('notes/café/01.md', 'notes/café') === true; existing pattern suite passes.
    • Effort/Risk/Confidence: S / LOW / HIGH.
  • P1-10 — Apply the note read cap to adoption, undo, and roadmap.

    • Evidence: src/storage/source-cap.ts:32 defines the 512 KiB cap; src/storage/loader.ts:343 declines oversized notes; src/cli/adopt.ts:574, src/cli/adopt-undo.ts:762, and src/cli/roadmap.ts:326 call readFileSync with no stat guard.
    • Impact: a >512 KiB note is adopted and reported as a topic, but every read command skips it forever; validation shows only a read-failure. A pathological file can also exhaust memory during a batch.
    • Fix: stat before read in all three write paths; refuse with oversizedNoteDiagnostic, exit 2 or 3 per the existing command contract; no write.
    • Tests: a 513 KiB note is refused with the size diagnostic and no frontmatter is written; a 511 KiB note still adopts.
    • Effort/Risk/Confidence: S / LOW / HIGH.
  • P1-11 — Make the timezone matrix actually vary the timezone.

    • Evidence: test/timezone-matrix.test.ts:1-77 never sets process.env.TZ or spawns under a foreign zone; test/application-record-review.test.ts:13-14 asserts updates.due_at equals formatLocalDateOnly(dueDate), the exact function under test.
    • Impact: under UTC CI, a getUTC*/toISOString regression in local-calendar due-date math passes green; the one suite named for timezones provides no cross-zone evidence.
    • Fix: run the date cases (or a spawned CLI case) with TZ=America/Los_Angeles and TZ=Asia/Kolkata, asserting literal YYYY-MM-DD values; replace the tautological assertion with a literal date; add platform guards only where the child TZ mechanism is unavailable.
    • Tests: the new cross-zone cases fail if computeDueDate is changed to UTC arithmetic; the existing UTC cases still pass.
    • Effort/Risk/Confidence: S / LOW / HIGH.
  • P1-12 — Correct the two README rows that contradict shipped behavior.

    • Evidence: README.md:153 says session start "starts AI tutor if provider is configured"; src/cli/session.ts:504-510 only reports ai_provider_configured and never calls the provider. README.md:187 says validate --fix repairs only SM-2; src/cli/validate.ts:72-80 dispatches rebuild_derived_views, recompute_topic_mastery, and reset_review_fields. test/docs-command-matrix.test.ts guards only docs/02-0 and 06-2, never the README.
    • Impact: users are told a Phase-2 feature runs and that --fix only touches SM-2, when it can rewrite derived views and mastery.
    • Fix: correct both rows; extend the docs matrix test to cover the README commands/options or add a targeted README test.
    • Tests: the corrected claims are asserted by a test that fails on the old wording/behavior mismatch.
    • Effort/Risk/Confidence: S / LOW / HIGH.
P2 — correctness, security hardening, performance, and test gaps
  • P2-01 — Settle confirmation prompts on EOF.

    • Evidence: src/cli/adopt.ts:69-82, src/cli/adopt-undo.ts:152-165, src/cli/roadmap.ts:1051-1058, and src/cli/config.ts:296-315 create readline question callbacks with no close handler; src/cli/session.ts:297-325 has the correct pattern.
    • Impact: Ctrl+D at a confirmation prompt leaves the promise pending; the process exits 0 having written nothing, so aborts and successes are indistinguishable.
    • Fix: resolve false (or null) on 'close'/EOF in all four helpers, modeled on session.ts.
    • Effort/Risk/Confidence: S / LOW / MED.
  • P2-02 — Do not reuse a completed session ID on session start.

    • Evidence: src/cli/session.ts:488-492 reuses activeSession.session_id on topic match alone; :614-647 treats that ID as already recorded; src/storage/memory.ts:510-516 intentionally returns on a lock conflict, so a completed marker can survive; hasCompletedSession exists at :1155-1165.
    • Impact: a stale marker makes the next session end print already recorded and discard the new study time/body.
    • Fix: reuse the marker's ID only when !hasCompletedSession(vault, id); otherwise rotate or clear the stale marker at start. Preserve the intentional repeated-start behavior for an in-progress session.
    • Effort/Risk/Confidence: M / MED / MED.
  • P2-03 — Refuse a symlinked per-target lock directory during recovery.

    • Evidence: src/storage/lock.ts:173-205 containment-checks only .palee/locks; :446 lists the per-target <hash>.lockdir with readdirSync, which follows a symlink; :516-551 unlinks/renames entries inside it.
    • Impact: a pre-planted lockdir symlink makes stale recovery delete or rename files in the link target outside the vault before rmdir fails.
    • Fix: lstat/canonicalize the lock directory itself and refuse a symlink or non-directory before listing or sweeping; keep the existing bounded-recovery behavior.
    • Effort/Risk/Confidence: S / LOW / MED.
  • P2-04 — Strip PALEE_API_KEY from the npm child environment.

    • Evidence: src/cli/update-check.ts:71-87 spreads options into spawnSync with no env, inheriting process.env; :553-565 uses the same path for npm install -g; src/cli/update.ts:59 invokes it.
    • Impact: npm and any lifecycle script can read the provider credential. Latent today because the dependency tree has no install scripts, but one dependency change away.
    • Fix: pass an explicit env to spawnNpm with PALEE_API_KEY removed, preserving PATH, HOME, proxy, and npm configuration variables; leave other PALEE_* behavior unchanged unless proven safe to strip.
    • Effort/Risk/Confidence: S / LOW / HIGH.
  • P2-05 — Parse PALEE_ALLOW_INSECURE_CONFIG_DIR as an affirmative value.

    • Evidence: src/cli/config.ts:642 disables the Windows guard for any non-empty value; :647 documents the override as =1.
    • Impact: =0 or =false, the conventional false spellings, still bypass the profile-location refusal and store the key in a share.
    • Fix: treat only 1/true as enabling; every other value keeps the guard active; update the error text if needed.
    • Effort/Risk/Confidence: S / LOW / HIGH.
  • P2-06 — Sanitize vault- and config-derived text at the output boundary.

    • Evidence: src/cli/plan.ts:217, src/cli/next.ts:151,169 print topic.title directly; src/storage/note-title.ts:52-57 strips newlines but not C0/C1 control characters; src/ai/provider.ts:188-196 already applies the intended policy.
    • Impact: a shared or cloned vault whose frontmatter/H1 title contains ANSI/OSC sequences can forge terminal output, hide warnings, or set the clipboard via OSC 52.
    • Fix: export one control-character sanitizer and apply it to all vault/config-derived display values at the output boundary, not only provider text.
    • Effort/Risk/Confidence: M / LOW / HIGH.
  • P2-07 — Scope Pages and OIDC permissions to the deploy job.

    • Evidence: .github/workflows/deploy-docs.yml:15-18 grants pages: write and id-token: write workflow-wide; :28 the build job inherits them while running the third-party VitePress/Mermaid/Katex toolchain; :60-71 only the deploy job needs Pages.
    • Impact: a compromised docs-era dependency executing during build can use the inherited Pages/OIDC credentials.
    • Fix: set contents: read at workflow level and add pages: write/id-token: write only to the deploy job.
    • Effort/Risk/Confidence: S / LOW / HIGH.
  • P2-08 — Compute the dependency quarantine once in plan.

    • Evidence: src/cli/plan.ts:87 calls findPrerequisiteBlocked, which builds the graph and calls quarantineCyclicTopics at src/application/get-next-topics.ts:141; src/cli/plan.ts:129 calls quarantineCyclicTopics again. Each pass rebuilds adjacency/Tarjan and can enumerate up to 1000 cycles.
    • Impact: the most expensive graph operation in the busiest planning command runs twice and allocates three topic maps.
    • Fix: return the computed acyclic/cycles from one pass and pass them to both the gate and the plan sections; keep next/dashboard behavior unchanged.
    • Effort/Risk/Confidence: S / LOW / HIGH.
  • P2-09 — Scan sessions once per rebuild.

    • Evidence: src/storage/memory.ts:735-772 scans all S-*.md to pick the newest, then :813 calls regenerateIndex, which scans the same directory again at :608-659; both read and YAML-parse every file.
    • Impact: every session end, draft save/discard and validate --fix pays O(2N) session reads/parses; sessions grow without bound.
    • Fix: collect rows and the newest session in one scan and pass the rows to the index writer (optional parameter; keep the exported regenerateIndex API).
    • Effort/Risk/Confidence: S / LOW / HIGH.
  • P2-10 — Cover the cache's later same-size edit branch.

    • Evidence: src/storage/cache.ts:153-165 (mtime changed outside the 2 s horizon, same size → re-hash and evict) has no dedicated test; test/storage-cache.test.ts:44-151 covers size change, same-size within the horizon, and same-second mtime fallback.
    • Impact: deleting or breaking the fingerprint comparison keeps the suite green while plan/next serve stale status/due/mastery for the life of the process.
    • Fix: set a cache entry, rewrite the file with equal byte length and a +5 s mtime, assert get() returns null; mirror with equal content to assert a hit.
    • Effort/Risk/Confidence: S / LOW / HIGH.
  • P2-11 — Make the Windows directory-fsync test assert the attempt.

    • Evidence: test/storage-atomic-write.test.ts:427-430 replaces the refusal assertion with assert.ok(true) on win32.
    • Impact: the #333 durability fallback can regress on the platform whose EPERM behavior it exists for while the suite stays green.
    • Fix: record that the post-rename directory open/fsync was attempted (the file already tracks directory fds) and assert the attempt; mark any genuinely untestable part as a tracked gap.
    • Effort/Risk/Confidence: S / LOW / HIGH.
  • P2-12 — Lazy-load command handlers.

    • Evidence: bin/palee.ts:15-28 statically imports all 14 handlers; measured node dist/bin/palee.js --version at 350-380 ms versus ~50 ms for bare Node on this checkout.
    • Impact: every interactive command pays the full import graph before dispatch.
    • Fix: keep Commander static and await import() each handler inside its .action callback; preserve the fatal/exit-code handler and maybeNotifyUpdate behavior.
    • Effort/Risk/Confidence: M / MED / MED.
P3 — cleanup and smaller contracts
  • P3-01 — Serialize config read-modify-write. src/cli/config.ts:548-550,655-657,741-743 load/mutate/save without a lock; saveConfig at :189-235 only makes the rename atomic. Concurrent config commands drop each other's fields. Add a config lock or snapshot/mtime check.
  • P3-02 — Make draft cleanup lock-aware and session-scoped. src/cli/session.ts:743 calls deleteTopicDrafts; src/storage/memory.ts:1182-1200 deletes every same-topic draft, and :1215-1239 unlinks without the target lock. Delete only drafts consumed by this session or take each target's lock.
  • P3-03 — Make the update-check cache write symlink-safe. src/cli/update-check.ts:313-319 uses writeFileSync on a predictable update-check.json; use an exclusive temp file plus rename (or O_NOFOLLOW) and verify the resolved path.
  • P3-04 — Redact JSON-escaped key spellings in provider errors. src/ai/provider.ts:188-196 masks only the literal secret; :590-597 acknowledges that JSON escaping changes the spelling. Mask both the raw and JSON.stringify-escaped forms before printing error/body text.
  • P3-05 — Extend the INV-29 guard to all socket primitives and bin/. test/ai-network-boundary.test.ts:30-40 misses http2, dgram, WebSocket, undici, and scans only src/; :58 limits the scan. Add the primitives and an explicit allowlist for the documented npm child process.
  • P3-06 — Stop loading and hashing full note bodies for read-only commands. src/storage/loader.ts:346-351 reads each body, :374-381 retains it, and :405-409 hashes it; next/plan/dashboard/progress never read .content. Add a metadata-only load path.
  • P3-07 — Avoid double YAML parsing in validate. src/validation/collect-vault.ts:66-93 calls scanNotes (parses at src/storage/scanner.ts:274) and then loadTopics (parses the same bytes at src/storage/loader.ts:351); loader.ts:405 also hashes injected content and discards it. Share the parsed frontmatter or skip the fingerprint in the snapshot branch.
  • P3-08 — Stop building dist/ for the coverage run. package.json:40-41 runs npm run build before test:coverage, but under NODE_V8_COVERAGE the suites spawn bin/palee.ts via tsx (test/palee-cli.ts:68-75) and no test reads dist. Keep the build for the npm test dist path and the release workflow.
  • P3-09 — Import one pillar-key list from the engine. Six copies: src/engine/mastery.ts:74, src/application/record-assessment.ts:13, src/cli/validate.ts:101, src/validation/rules/valid-assessment-fields.ts:19, src/validation/rules/valid-topic-mastery.ts:34, src/cli/adopt-undo.ts:67.
  • P3-10 — Centralize the topic scaffold and owned-key list. Four copies: src/cli/adopt.ts:408,1406, src/cli/roadmap.ts:276, src/cli/adopt-undo.ts:60, src/validation/rules/valid-review-fields.ts:72. Add one builder and one owned-key set.
  • P3-11 — Share one batch-scope predicate. src/cli/adopt.ts:618,629 and src/cli/adopt-undo.ts:795 implement the same include/exclude/tag decision with separate accounting; extract the predicate and keep each command's counters.
  • P3-12 — Move diagnostic formatting below both layers. src/engine/sm2.ts:12 imports ../validation/rules/diagnostic-value, while src/validation/rules/valid-topic-mastery.ts:29 imports the engine; relocate formatDiagnosticValue to a neutral module.
  • P3-13 — Judge non-string palee_id one way. src/cli/adopt.ts:339 treats a truthy non-string as already adopted; :599-610 only validates under --auto-chain; src/cli/adopt-undo.ts:204 treats it as not a topic; src/storage/loader.ts:353 ignores it. Route all scans through isValidPaleeId and report one repair path.
  • P3-14 — Split the god modules. src/cli/adopt.ts 1513 lines, src/engine/toc-chain.ts 1330, src/storage/memory.ts 1267, src/engine/auto-chain.ts 1094, src/cli/roadmap.ts 1073. Extract scan/plan/execute phases and split memory into hot/index, sessions, and drafts behind the existing barrel.
  • P3-15 — Verify the tarball on the local publish path. package.json:27 runs only npm run build in prepublishOnly; scripts/verify-tarball.js exists but is invoked only by release.yml:72. Add the tarball assertion to the local publish path or document the verified-CI-only policy.
  • P3-16 — Build docs on the Node version CI gates. .github/workflows/ci.yml:61-64 builds docs on Node 24; deploy-docs.yml:41 rebuilds on Node 22. Every docs merge builds twice, and the deploy runtime is not covered by PR checks. Use Node 24 in both or deploy the CI artifact.
  • P3-17 — Include application and validation suites in test:unit. package.json:33 globs only storage/engine/types/smoke; 6 application and 12 validation test files are outside it. Add them or rename the script to test:core.
  • P3-18 — Make agent instructions discoverable. agent.md:1 is the only 18 KB conventions document; no root AGENTS.md/CLAUDE.md exists, and README.md:310 still says contribution guidelines will be added. Add a pointer file and real clone-to-green steps.
Direction — options, not defects (do not check off as bugs)
  • D-01 Named vault profiles or a per-run --vault. Evidence: bin/palee.ts:42-59 tells users to set PALEE_CONFIG_DIR per run to isolate vaults; src/types.ts:382 carries one vaultPath. Tradeoff: config precedence must stay backward-compatible; medium effort.
  • D-02 Publish the application read models. Evidence: src/index.ts:20-26 exports types/engine/storage/validation but not application/; ADR-0025:45 anticipates future interfaces calling getNextTopics/summarizeMastery. Additive, but exported names become compatibility commitments.
  • D-03 Machine-readable output for mutating commands. Evidence: README.md:284 says the remaining commands are human-only; bin/palee.ts:171,180 declare no --json for review/assess; src/application/record-review.ts:111 already returns a structured update. Each document needs a stable shape.
  • D-04 Batch assessment input. Evidence: bin/palee.ts:180 takes one topic plus four score flags; src/application/record-assessment.ts:82 computes one update. A --from file would need dry-run, confirmation, and exit-1 partial-failure semantics.
Considered and rejected (so they are not re-audited)
  • CI never runs npm test; the dist-entry guard skips under c8 — rejected: the smoke-install job builds, packs, installs, and runs the real dist on three OSes, and the coverage-time source launch is deliberate per ADR-0029.
  • Extract shared adopt/undo helpers — deferred as a duplicate of P1-03 and P2-01; fix the two defects first.
  • Roadmap difficulty default diverges from adoption — rejected: both resolve undefined to intermediate; the scaffold duplication is real (P3-10) but not a behavior drift.
  • Dependency audit — npm audit reports 0 advisories; the vite/mermaid/katex/dompurify overrides are confined to the docs toolchain.
Dependency ordering
  • P1-04 before P2-09 (both edit rebuildHotAndIndex/regenerateIndex).
  • P1-07 before P2-03 if both are selected (both edit lock.ts).
  • P1-01 before P1-11 (the timezone tests should pin the capped behavior).
  • P1-02 and P1-03 are independent files but share the adoption write-path theme; land both before P3-10/P3-11 refactors.
Not audited

VitePress page content beyond spot checks, architecture/ artifacts, examples/, dist/, full test execution (typecheck/lint only), and dependency provenance beyond npm audit. Standard-depth audit, not deep.

Area

Cross-cutting: Core engine (src/engine/), Storage / vault scanning (src/storage/), Session memory, CLI / bin layer, Tests, Config / migration, AI / Phase 2 (src/ai/), Build / CI, Documentation.

Estimated effort

P1: roughly 2-3 focused days total (all 12 are S). P2: roughly 3-4 days (two M items). P3: roughly 4-6 days, mostly cleanup and refactors. Direction options are separate spikes, not included.

AI acknowledgment

This issue was produced from an AI-assisted, read-only audit at the maintainer's explicit request. Every finding was re-opened and confirmed in source at 24947bc before publication. The maintainer chose full public detail for the security findings after the public-visibility warning. No secret values appear in this report.

Dominant language
TypeScript
Stars
1
Forks
0
Avg merge
13h 46m
Merged PRs (30d)
158

Getting set up

  • No Dockerfile or Docker Compose file
  • Has a pull request template
  • No contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from Kuldeep2822k/cli

All issues in Kuldeep2822k/cli

Similar issues

More TypeScript issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.