jfox-cli 1.16.1__tar.gz → 1.16.2__tar.gz
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.
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/CHANGELOG.md +10 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/PKG-INFO +1 -1
- jfox_cli-1.16.2/docs/superpowers/plans/2026-09-24-issue-549-twin-files.md +305 -0
- jfox_cli-1.16.2/docs/superpowers/specs/2026-09-24-issue-549-twin-files-design.md +121 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/jfox/__init__.py +1 -1
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/jfox/moc/cli.py +3 -3
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/jfox/models.py +32 -4
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/jfox/note.py +23 -7
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/jfox/redirect.py +3 -2
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/pyproject.toml +1 -1
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/tests/integration/test_moc_member_commands.py +3 -2
- jfox_cli-1.16.2/tests/integration/test_stale_name_single_file.py +306 -0
- jfox_cli-1.16.2/tests/unit/test_note_path_rules.py +111 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/uv.lock +1 -1
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/.claude/skills/ci/SKILL.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/.claude/skills/release/SKILL.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/.claude/skills/release/release_helper.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/.claude/skills/release-all/SKILL.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/.claude/skills/release-all/release_all_helper.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/.claude/skills/release-cc-plugin/SKILL.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/.claude/skills/release-cc-plugin/release_cc_plugin_helper.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/.claude/skills/release-kimi-plugin/SKILL.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/.claude/skills/release-kimi-plugin/release_kimi_plugin_helper.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/.claude-plugin/marketplace.json +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/.githooks/pre-push +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/.github/ISSUE_TEMPLATE/bug_report.yml +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/.github/ISSUE_TEMPLATE/config.yml +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/.github/ISSUE_TEMPLATE/docs_improvement.yml +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/.github/ISSUE_TEMPLATE/feature_request.yml +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/.github/workflows/integration-test.yml +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/.github/workflows/publish.yml +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/.gitignore +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/.markdownlint-cli2.jsonc +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/.pi-lens.json +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/.python-version +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/AGENTS.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/CLAUDE.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/DEVELOPMENT_PLAN.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/README.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/SESSION.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/SESSION_SUMMARY.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/docs/claude-code-plugin-guide.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/docs/cli-descriptions.yaml +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/docs/cli-reference.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/docs/installation.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/docs/json-schemas.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/docs/nightly-test.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/docs/plugin-inventory.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/docs/superpowers/plans/2026-04-11-bulk-import-bm25-fix.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/docs/superpowers/plans/2026-04-11-edit-command.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/docs/superpowers/plans/2026-04-11-unify-format-option.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/docs/superpowers/plans/2026-04-12-ci-coverage-optimization.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/docs/superpowers/plans/2026-04-12-edit-content-file.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/docs/superpowers/plans/2026-04-12-fix-index-rebuild-clear.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/docs/superpowers/plans/2026-04-12-fix-index-verify-id-mismatch.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/docs/superpowers/plans/2026-04-12-fix-jfox-health-skill-kb-param.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/docs/superpowers/plans/2026-04-12-index-kb-param.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/docs/superpowers/plans/2026-04-12-lazy-import-perf.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/docs/superpowers/plans/2026-04-12-skill-redesign.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/docs/superpowers/plans/2026-04-13-add-content-file.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/docs/superpowers/plans/2026-04-13-bulk-import-vectorstore-init.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/docs/superpowers/plans/2026-04-13-ingest-log-command.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/docs/superpowers/plans/2026-04-13-ingest-skill-sync.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/docs/superpowers/plans/2026-04-13-suppress-third-party-logging.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/docs/superpowers/plans/2026-04-13-sync-skills-with-cli.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/docs/superpowers/plans/2026-04-14-sync-docs-daemon-show.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/docs/superpowers/plans/2026-04-15-gpu-embedding.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/docs/superpowers/plans/2026-04-15-make-daemon-deps-required.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/docs/superpowers/plans/2026-04-15-session-summary-confirmation.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/docs/superpowers/plans/2026-04-16-fix-windows-daemon-console-window.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/docs/superpowers/plans/2026-04-18-daemon-timeout-and-index-rebuild.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/docs/superpowers/plans/2026-04-22-release-skill.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/docs/superpowers/plans/2026-04-26-fix-daemon-deprecation-warnings.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/docs/superpowers/plans/2026-04-27-intranet-model-download.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/docs/superpowers/plans/2026-04-28-tag-filtering.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/docs/superpowers/plans/2026-04-29-daemon-stop-fix.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/docs/superpowers/plans/2026-05-03-jfox-check.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/docs/superpowers/plans/2026-05-03-list-notes-skip-summary.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/docs/superpowers/plans/2026-05-03-log-level-fix.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/docs/superpowers/plans/2026-05-04-list-notes-index.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/docs/superpowers/plans/2026-05-06-atomic-write.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/docs/superpowers/plans/2026-05-09-session-note-type.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/docs/superpowers/plans/2026-05-10-jfox-plugin.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/docs/superpowers/plans/2026-05-19-fix-last-used-never.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/docs/superpowers/plans/2026-05-20-modelscope-migration.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/docs/superpowers/plans/2026-06-02-todo-note-type.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/docs/superpowers/plans/2026-06-16-kimi-code-auto-summary.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/docs/superpowers/plans/2026-06-17-index-rebuild-backlinks.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/docs/superpowers/plans/2026-06-17-using-jfox-skill.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/docs/superpowers/plans/2026-06-18-self-update-command.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/docs/superpowers/plans/2026-06-21-fragment-capture.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/docs/superpowers/plans/2026-06-21-gem-synthesis.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/docs/superpowers/plans/2026-06-25-gem-synth-throttle-ledger.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/docs/superpowers/plans/2026-07-08-auto-summary-schedule-window.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/docs/superpowers/plans/2026-07-08-candidate-promote.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/docs/superpowers/plans/2026-07-12-gem-synth-dedup.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/docs/superpowers/plans/2026-07-12-release-cc-plugin-skill.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/docs/superpowers/plans/2026-07-12-session-summary-streamline.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/docs/superpowers/plans/2026-07-12-show-json.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/docs/superpowers/plans/2026-07-17-fix-candidate-dual-h1.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/docs/superpowers/plans/2026-07-18-dedup-lifecycle-decouple.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/docs/superpowers/plans/2026-07-19-promote-skill-three-modes.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/docs/superpowers/plans/2026-07-25-bookshelf.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/docs/superpowers/plans/2026-07-25-gem-synth-dedup-incremental-merge.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/docs/superpowers/plans/2026-07-26-kb-backup-restore.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/docs/superpowers/plans/2026-07-26-promote-skill-clear-reports-rewrite.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/docs/superpowers/plans/2026-07-27-release-all.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/docs/superpowers/plans/2026-07-28-bookshelf-flat-bundle.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/docs/superpowers/plans/2026-07-28-nightly-fulltest.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/docs/superpowers/plans/2026-08-07-session-to-permanent-cc-kimi.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/docs/superpowers/plans/2026-08-11-permanent-note-template.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/docs/superpowers/plans/2026-08-12-noteindex-frontmatter-limit-drop.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/docs/superpowers/plans/2026-08-15-delete-backlinks-cleanup.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/docs/superpowers/plans/2026-08-16-bm25-concurrent-write-race.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/docs/superpowers/plans/2026-08-21-index-verify-legacy-filenames.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/docs/superpowers/plans/2026-08-21-moc-density-diagnose.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/docs/superpowers/plans/2026-08-22-bm25-type-debt-cleanup.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/docs/superpowers/plans/2026-08-22-issue-392-cr-leftovers.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/docs/superpowers/plans/2026-08-22-jfox-moc-skill.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/docs/superpowers/plans/2026-08-22-markdownlint-adoption.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/docs/superpowers/plans/2026-08-22-moc-create-update.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/docs/superpowers/plans/2026-08-25-moc-create-update-json-shorthand.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/docs/superpowers/plans/2026-08-27-issue-436-organize-archive.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/docs/superpowers/plans/2026-08-28-cpu-default-model-bge-small-zh.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/docs/superpowers/plans/2026-08-29-english-readme-baseline.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/docs/superpowers/plans/2026-08-29-issue-383-add-permanent-dedup.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/docs/superpowers/plans/2026-08-29-louvain-moc-clustering.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/docs/superpowers/plans/2026-08-30-gem-synth-prompt-judgment.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/docs/superpowers/plans/2026-08-30-test-config-isolation.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/docs/superpowers/plans/2026-08-31-deterministic-cli-reference.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/docs/superpowers/plans/2026-09-01-ci-docs-drift-gate.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/docs/superpowers/plans/2026-09-02-plugin-inventory-enum-checks.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/docs/superpowers/plans/2026-09-03-jfox-moc-batch-quality-chapter.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/docs/superpowers/plans/2026-09-03-moc-member-management.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/docs/superpowers/plans/2026-09-04-chromadb-dim-migration-ndarray.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/docs/superpowers/plans/2026-09-05-moc-diagnose-archived-orphan.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/docs/superpowers/plans/2026-09-07-bm25-fresh-kb-logging.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/docs/superpowers/plans/2026-09-07-pi-prompt-capture.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/docs/superpowers/plans/2026-09-09-from-dict-non-dict-guard.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/docs/superpowers/plans/2026-09-09-nightly-test-failures.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/docs/superpowers/plans/2026-09-10-skill-sync-399-overview-jfox-judge.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/docs/superpowers/plans/2026-09-14-json-schema-unify.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/docs/superpowers/plans/2026-09-14-lightweight-install.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/docs/superpowers/plans/2026-09-14-wiki-link-self-link.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/docs/superpowers/plans/2026-09-23-h1-strip-normalization.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/docs/superpowers/plans/2026-09-24-adjacent-backticks-swallow-wiki-links.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/docs/superpowers/specs/2026-04-03-bugfixes-design.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/docs/superpowers/specs/2026-04-12-skill-redesign-design.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/docs/superpowers/specs/2026-04-13-pr-auto-code-review-design.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/docs/superpowers/specs/2026-04-14-show-command-design.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/docs/superpowers/specs/2026-04-15-gpu-embedding-design.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/docs/superpowers/specs/2026-04-15-session-summary-confirmation-design.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/docs/superpowers/specs/2026-04-21-release-skill-design.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/docs/superpowers/specs/2026-04-26-fix-daemon-deprecation-warnings-design.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/docs/superpowers/specs/2026-04-27-intranet-model-download-design.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/docs/superpowers/specs/2026-04-28-tag-filtering-design.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/docs/superpowers/specs/2026-04-29-daemon-stop-fix-design.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/docs/superpowers/specs/2026-04-30-jfox-kb-env-var-design.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/docs/superpowers/specs/2026-05-03-jfox-check-design.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/docs/superpowers/specs/2026-05-03-list-notes-skip-summary-design.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/docs/superpowers/specs/2026-05-03-log-level-fix-design.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/docs/superpowers/specs/2026-05-04-list-notes-index-design.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/docs/superpowers/specs/2026-05-06-atomic-write-design.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/docs/superpowers/specs/2026-05-09-jfox-plugin-design.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/docs/superpowers/specs/2026-05-09-session-note-type-design.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/docs/superpowers/specs/2026-05-20-modelscope-migration-design.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/docs/superpowers/specs/2026-06-02-todo-note-type-design.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/docs/superpowers/specs/2026-06-16-kimi-code-auto-summary-design.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/docs/superpowers/specs/2026-06-17-index-rebuild-backlinks-design.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/docs/superpowers/specs/2026-06-17-using-jfox-skill-design.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/docs/superpowers/specs/2026-06-18-archive-soft-delete-design.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/docs/superpowers/specs/2026-06-18-self-update-command-design.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/docs/superpowers/specs/2026-06-21-fragment-capture-design.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/docs/superpowers/specs/2026-06-21-gem-synthesis-design.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/docs/superpowers/specs/2026-06-25-gem-synth-throttle-ledger-design.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/docs/superpowers/specs/2026-07-08-candidate-promote-design.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/docs/superpowers/specs/2026-07-12-gem-synth-dedup-design.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/docs/superpowers/specs/2026-07-12-release-cc-plugin-skill-design.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/docs/superpowers/specs/2026-07-12-session-summary-streamline-design.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/docs/superpowers/specs/2026-07-12-show-json-design.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/docs/superpowers/specs/2026-07-18-dedup-lifecycle-decouple-design.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/docs/superpowers/specs/2026-07-19-promote-skill-three-modes-design.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/docs/superpowers/specs/2026-07-25-bookshelf-design.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/docs/superpowers/specs/2026-07-25-gem-synth-dedup-incremental-merge-design.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/docs/superpowers/specs/2026-07-26-kb-backup-restore-design.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/docs/superpowers/specs/2026-07-26-promote-skill-clear-reports-rewrite-design.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/docs/superpowers/specs/2026-07-27-release-all-orchestration-design.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/docs/superpowers/specs/2026-07-28-bookshelf-flat-bundle-design.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/docs/superpowers/specs/2026-07-28-nightly-fulltest-design.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/docs/superpowers/specs/2026-08-07-session-to-permanent-cc-kimi-design.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/docs/superpowers/specs/2026-08-11-permanent-note-template-design.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/docs/superpowers/specs/2026-08-12-noteindex-frontmatter-limit-drop-design.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/docs/superpowers/specs/2026-08-15-delete-backlinks-cleanup-design.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/docs/superpowers/specs/2026-08-16-bm25-concurrent-write-race-design.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/docs/superpowers/specs/2026-08-20-bm25-clear-orphan-followup-design.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/docs/superpowers/specs/2026-08-21-index-verify-legacy-filenames-design.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/docs/superpowers/specs/2026-08-21-moc-density-diagnose-design.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/docs/superpowers/specs/2026-08-22-bm25-type-debt-cleanup-design.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/docs/superpowers/specs/2026-08-22-issue-392-cr-leftovers-design.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/docs/superpowers/specs/2026-08-22-jfox-moc-skill-design.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/docs/superpowers/specs/2026-08-22-markdownlint-adoption-design.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/docs/superpowers/specs/2026-08-22-moc-create-update-design.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/docs/superpowers/specs/2026-08-25-moc-create-update-json-shorthand-design.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/docs/superpowers/specs/2026-08-27-issue-436-organize-archive-design.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/docs/superpowers/specs/2026-08-27-redirect-and-delete-guard.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/docs/superpowers/specs/2026-08-28-cpu-default-model-bge-small-zh-design.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/docs/superpowers/specs/2026-08-29-english-readme-baseline-design.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/docs/superpowers/specs/2026-08-29-issue-383-add-permanent-dedup-design.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/docs/superpowers/specs/2026-08-29-louvain-moc-clustering-design.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/docs/superpowers/specs/2026-08-30-gem-synth-prompt-judgment-design.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/docs/superpowers/specs/2026-08-30-test-config-isolation-design.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/docs/superpowers/specs/2026-08-31-deterministic-cli-reference-design.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/docs/superpowers/specs/2026-09-01-ci-docs-drift-gate-design.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/docs/superpowers/specs/2026-09-02-plugin-inventory-enum-checks-design.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/docs/superpowers/specs/2026-09-03-jfox-moc-batch-quality-chapter-design.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/docs/superpowers/specs/2026-09-03-moc-member-management-design.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/docs/superpowers/specs/2026-09-04-chromadb-dim-migration-ndarray-design.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/docs/superpowers/specs/2026-09-05-moc-diagnose-archived-orphan-design.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/docs/superpowers/specs/2026-09-07-bm25-fresh-kb-logging-design.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/docs/superpowers/specs/2026-09-07-pi-prompt-capture-design.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/docs/superpowers/specs/2026-09-09-from-dict-non-dict-guard-design.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/docs/superpowers/specs/2026-09-09-nightly-test-failures-design.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/docs/superpowers/specs/2026-09-10-lightweight-install-embed-extra-design.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/docs/superpowers/specs/2026-09-10-skill-sync-399-overview-jfox-judge-design.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/docs/superpowers/specs/2026-09-14-json-schema-unify-design.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/docs/superpowers/specs/2026-09-14-wiki-link-self-link-design.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/docs/superpowers/specs/2026-09-23-h1-strip-normalization-design.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/docs/superpowers/specs/2026-09-24-adjacent-backticks-swallow-wiki-links-design.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/docs/troubleshooting.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/jessica-jones-static-cable.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/jfox/__main__.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/jfox/add_dedup.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/jfox/auto_summary/__init__.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/jfox/auto_summary/cli.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/jfox/auto_summary/extractor.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/jfox/auto_summary/kimi_source.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/jfox/auto_summary/ledger.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/jfox/auto_summary/loop.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/jfox/auto_summary/runner.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/jfox/auto_summary/scanner.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/jfox/auto_summary/schedule.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/jfox/auto_summary/sources.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/jfox/backup/__init__.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/jfox/backup/cli.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/jfox/backup/loop.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/jfox/backup/manager.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/jfox/backup/schedule.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/jfox/bm25_index.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/jfox/bookshelf/__init__.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/jfox/bookshelf/cli.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/jfox/bookshelf/meta.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/jfox/bookshelf/store.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/jfox/candidates/__init__.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/jfox/candidates/cli.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/jfox/candidates/service.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/jfox/cli.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/jfox/config.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/jfox/daemon/__init__.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/jfox/daemon/__main__.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/jfox/daemon/client.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/jfox/daemon/process.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/jfox/daemon/server.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/jfox/dedup.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/jfox/dedup_lifecycle.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/jfox/embedding_backend.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/jfox/embedding_migration.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/jfox/formatters.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/jfox/fragment/__init__.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/jfox/fragment/cli.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/jfox/fragment/internal_sources.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/jfox/fragment/service.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/jfox/fragment/store.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/jfox/git_extractor.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/jfox/global_config.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/jfox/graph.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/jfox/indexer.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/jfox/kb_manager.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/jfox/moc/__init__.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/jfox/moc/cluster.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/jfox/moc/draft.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/jfox/moc/generate.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/jfox/model_downloader.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/jfox/note_index.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/jfox/performance.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/jfox/prompts/__init__.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/jfox/prompts/actions.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/jfox/prompts/cli.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/jfox/prompts/grounding.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/jfox/prompts/judge.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/jfox/prompts/lifecycle.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/jfox/prompts/runner.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/jfox/prompts/service.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/jfox/prompts/store.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/jfox/prompts/transcript.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/jfox/search_engine.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/jfox/template.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/jfox/template_cli.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/jfox/utils.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/jfox/vector_store.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/package.json +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/packages/cc-plugin/.claude-plugin/plugin.json +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/packages/cc-plugin/hooks/fragment-capture.sh +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/packages/cc-plugin/hooks/hooks.json +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/packages/cc-plugin/skills/bookshelf/SKILL.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/packages/cc-plugin/skills/ingest/SKILL.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/packages/cc-plugin/skills/manage/SKILL.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/packages/cc-plugin/skills/organize/SKILL.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/packages/cc-plugin/skills/promote/SKILL.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/packages/cc-plugin/skills/search/SKILL.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/packages/cc-plugin/skills/session-summary/SKILL.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/packages/cc-plugin/skills/session-to-permanent/SKILL.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/packages/cc-plugin/skills/using-jfox/SKILL.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/packages/kimi-plugin/README.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/packages/kimi-plugin/kimi.plugin.json +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/packages/kimi-plugin/skills/jfox-admin/SKILL.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/packages/kimi-plugin/skills/jfox-auto-summary/SKILL.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/packages/kimi-plugin/skills/jfox-ingest/SKILL.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/packages/kimi-plugin/skills/jfox-manage/SKILL.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/packages/kimi-plugin/skills/jfox-organize/SKILL.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/packages/kimi-plugin/skills/jfox-promote/SKILL.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/packages/kimi-plugin/skills/jfox-search/SKILL.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/packages/kimi-plugin/skills/jfox-session-summary/SKILL.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/packages/kimi-plugin/skills/jfox-session-to-permanent/SKILL.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/packages/kimi-plugin/skills/jfox-template/SKILL.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/packages/kimi-plugin/skills/using-jfox/SKILL.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/packages/pi-plugin/README.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/packages/pi-plugin/extensions/jfox-prompt-capture.ts +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/packages/pi-plugin/test/run-tests.ts +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/pytest.ini +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/run_full_test.ps1 +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/scripts/LOUVAIN_VERIFY.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/scripts/download-model-intranet.sh +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/scripts/enum_coverage.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/scripts/generate_docs.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/scripts/louvain_verify.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/scripts/nightly_test.sh +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/scripts/nightly_test_helpers.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/scripts/plugin_inventory.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/scripts/verify_lightweight_install.sh +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/skills-recommend/README.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/skills-recommend/kimi-cli/jfox-common/SKILL.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/skills-recommend/kimi-cli/jfox-ingest/SKILL.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/skills-recommend/kimi-cli/jfox-organize/SKILL.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/skills-recommend/kimi-cli/jfox-search/SKILL.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/skills-recommend/kimi-cli/jfox-session-summary/SKILL.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/skills-recommend/pi/jfox-bookshelf/SKILL.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/skills-recommend/pi/jfox-ci/SKILL.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/skills-recommend/pi/jfox-common/SKILL.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/skills-recommend/pi/jfox-ingest/SKILL.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/skills-recommend/pi/jfox-judge/SKILL.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/skills-recommend/pi/jfox-moc/SKILL.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/skills-recommend/pi/jfox-organize/SKILL.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/skills-recommend/pi/jfox-overview/SKILL.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/skills-recommend/pi/jfox-promote/SKILL.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/skills-recommend/pi/jfox-release/SKILL.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/skills-recommend/pi/jfox-release-all/SKILL.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/skills-recommend/pi/jfox-release-cc-plugin/SKILL.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/skills-recommend/pi/jfox-release-kimi-plugin/SKILL.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/skills-recommend/pi/jfox-search/SKILL.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/skills-recommend/pi/jfox-session-summary/SKILL.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/skills-recommend/pi/jfox-session-to-permanent/SKILL.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/tests/COVERAGE_PLAN.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/tests/MIGRATION.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/tests/TESTS.md +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/tests/conftest.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/tests/fixtures/bulk_import_notes.json +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/tests/integration/__init__.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/tests/integration/test_auto_summary_kimi.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/tests/integration/test_backlinks.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/tests/integration/test_candidate_promote_flow.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/tests/integration/test_cli_reference_generation.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/tests/integration/test_edit_roundtrip.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/tests/integration/test_fragment_capture_flow.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/tests/integration/test_index_rebuild_backlinks.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/tests/integration/test_links_edit_add.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/tests/integration/test_model_download.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/tests/integration/test_no_embed_degradation.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/tests/integration/test_plugin_inventory.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/tests/integration/test_tag_filter_cli.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/tests/performance/__init__.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/tests/performance/test_performance.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/tests/test_add_dedup_cli.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/tests/test_advanced_features.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/tests/test_cli_format.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/tests/test_config_set_unit.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/tests/test_config_unit.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/tests/test_core_workflow.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/tests/test_embedding_device.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/tests/test_hybrid_search.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/tests/test_integration.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/tests/test_json_schema_contract.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/tests/test_kb_current.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/tests/test_suggest_links.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/tests/unit/__init__.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/tests/unit/bookshelf/__init__.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/tests/unit/bookshelf/conftest.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/tests/unit/bookshelf/test_cli.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/tests/unit/bookshelf/test_meta.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/tests/unit/bookshelf/test_store.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/tests/unit/conftest.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/tests/unit/test_add_backfill_links.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/tests/unit/test_add_dedup.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/tests/unit/test_archive.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/tests/unit/test_atomic_write.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/tests/unit/test_auto_summary_cli.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/tests/unit/test_auto_summary_config_sources.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/tests/unit/test_auto_summary_defaults.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/tests/unit/test_auto_summary_extractor.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/tests/unit/test_auto_summary_ledger.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/tests/unit/test_auto_summary_loop.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/tests/unit/test_auto_summary_runner.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/tests/unit/test_auto_summary_scanner.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/tests/unit/test_auto_summary_schedule.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/tests/unit/test_auto_summary_status.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/tests/unit/test_backup.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/tests/unit/test_bm25_batch.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/tests/unit/test_bm25_concurrency.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/tests/unit/test_bm25_fresh_kb_logging.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/tests/unit/test_candidates_migrated_cli.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/tests/unit/test_check.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/tests/unit/test_conftest_mock.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/tests/unit/test_content_file.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/tests/unit/test_daemon_cli.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/tests/unit/test_daemon_process.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/tests/unit/test_dedup_lifecycle.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/tests/unit/test_default_model_switch.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/tests/unit/test_delete_backlink_cleanup.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/tests/unit/test_delete_guard.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/tests/unit/test_edit.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/tests/unit/test_embed_availability.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/tests/unit/test_embedding_backend_daemon_only.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/tests/unit/test_embedding_local_load.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/tests/unit/test_embedding_migration.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/tests/unit/test_enum_coverage.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/tests/unit/test_format_unify.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/tests/unit/test_formatters.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/tests/unit/test_fragment_cli.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/tests/unit/test_fragment_config.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/tests/unit/test_fragment_hook.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/tests/unit/test_fragment_internal_sources.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/tests/unit/test_fragment_service.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/tests/unit/test_fragment_store.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/tests/unit/test_generate_docs.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/tests/unit/test_git_extractor.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/tests/unit/test_global_config.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/tests/unit/test_index_kb_param.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/tests/unit/test_indexer_clear_before_rebuild.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/tests/unit/test_indexer_verify.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/tests/unit/test_json_mode.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/tests/unit/test_kb_manager.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/tests/unit/test_kimi_extractor.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/tests/unit/test_kimi_plugin_skills.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/tests/unit/test_kimi_scanner.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/tests/unit/test_lazy_import.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/tests/unit/test_ledger_migration.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/tests/unit/test_list_notes_skip.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/tests/unit/test_logging_config.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/tests/unit/test_moc_cli.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/tests/unit/test_moc_cluster.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/tests/unit/test_moc_create_cli.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/tests/unit/test_moc_draft.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/tests/unit/test_moc_generate.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/tests/unit/test_moc_integration.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/tests/unit/test_moc_member_cli.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/tests/unit/test_moc_skill_docs.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/tests/unit/test_moc_update_cli.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/tests/unit/test_model_downloader.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/tests/unit/test_nightly_test_helpers.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/tests/unit/test_note_add_config.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/tests/unit/test_note_candidate_fields.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/tests/unit/test_note_candidate_filename.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/tests/unit/test_note_generator.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/tests/unit/test_note_index.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/tests/unit/test_note_lifecycle_hooks.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/tests/unit/test_note_promote.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/tests/unit/test_note_type_candidate.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/tests/unit/test_note_type_structure.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/tests/unit/test_plugin_inventory.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/tests/unit/test_prompt_actions.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/tests/unit/test_prompt_backfill.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/tests/unit/test_prompt_candidate.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/tests/unit/test_prompt_capture.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/tests/unit/test_prompt_cli.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/tests/unit/test_prompt_grounding.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/tests/unit/test_prompt_judge.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/tests/unit/test_prompt_retirement.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/tests/unit/test_prompt_runner.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/tests/unit/test_prompt_store.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/tests/unit/test_prompt_transcript.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/tests/unit/test_pyproject_embed_extra.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/tests/unit/test_rebuild_backlinks_impl.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/tests/unit/test_redirect.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/tests/unit/test_release_all_helper.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/tests/unit/test_release_cc_plugin_helper.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/tests/unit/test_release_helper.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/tests/unit/test_release_helper_verify.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/tests/unit/test_release_kimi_plugin_helper.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/tests/unit/test_runner_multi_source.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/tests/unit/test_scanner_list.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/tests/unit/test_search_engine_embed_warning.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/tests/unit/test_session_file_source.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/tests/unit/test_session_note.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/tests/unit/test_session_source.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/tests/unit/test_show.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/tests/unit/test_system_prompt_sections.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/tests/unit/test_tag_filter.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/tests/unit/test_template.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/tests/unit/test_template_cli.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/tests/unit/test_update.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/tests/unit/test_use_kb_env_var.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/tests/unit/test_using_jfox_skill.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/tests/unit/test_vector_store_clear.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/tests/unit/test_vector_store_dimension_warning.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/tests/unit/test_vector_store_embed_guard.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/tests/unit/test_vector_store_embeddings.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/tests/unit/test_wiki_link_resolution.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/tests/utils/__init__.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/tests/utils/assertions.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/tests/utils/jfox_cli.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/tests/utils/note_generator.py +0 -0
- {jfox_cli-1.16.1 → jfox_cli-1.16.2}/tests/utils/temp_kb.py +0 -0
|
@@ -2,6 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to jfox-cli will be documented in this file.
|
|
4
4
|
|
|
5
|
+
## [1.16.2] - 2026-09-25
|
|
6
|
+
|
|
7
|
+
### Fixes
|
|
8
|
+
|
|
9
|
+
- **note**: 加载笔记钉住真实磁盘路径,update_note 规范化写——消除同 ID 双文件 (#549) (#553)
|
|
10
|
+
|
|
11
|
+
[1.16.2]: https://github.com/zhuxixi/jfox/compare/v1.16.1...v1.16.2
|
|
12
|
+
|
|
5
13
|
## [1.16.1] - 2026-09-24
|
|
6
14
|
|
|
7
15
|
### Fixes
|
|
@@ -18,6 +26,8 @@ All notable changes to jfox-cli will be documented in this file.
|
|
|
18
26
|
|
|
19
27
|
### Fixes
|
|
20
28
|
|
|
29
|
+
- **note**: 加载的笔记钉住真实磁盘路径——文件名与当前字段分家时,补 backlink / rebuild --backlinks / MOC 成员回填不再写出同 ID 双文件,`show`/`delete` 也能正确读写该笔记;`update_note` 改为显式规范化写并在成功后重钉路径 (#549)
|
|
30
|
+
|
|
21
31
|
- **note_index**: 剥离改单遍合并正则——反引号包 HTML 注释不再吞后续 wiki link (#548) (#550)
|
|
22
32
|
|
|
23
33
|
## [1.16.0] - 2026-09-23
|
|
@@ -0,0 +1,305 @@
|
|
|
1
|
+
# #549 同 ID 双文件修复 实现计划
|
|
2
|
+
|
|
3
|
+
> **For agentic workers:** Steps use checkbox (`- [ ]`) syntax to track progress. Follow instructions exactly.
|
|
4
|
+
|
|
5
|
+
**Spec:** `docs/superpowers/specs/2026-09-24-issue-549-twin-files-design.md`(commit 47b236c)
|
|
6
|
+
|
|
7
|
+
**Work from:** `/home/elling/git-repo/github/jfox/.pi/worktrees/issue-549-stale-name-twin-files`(所有路径以此为根;禁止碰主 checkout)
|
|
8
|
+
|
|
9
|
+
**技术摘要:** 路径语义两分法——`from_markdown` 钉住真实路径(`save_note` 就地写)+ `update_note` 写盘目标改用 `expected_filepath`(规范化写 + 成功后重钉 pin)。
|
|
10
|
+
|
|
11
|
+
## 验收归属(spec 矩阵 → 任务)
|
|
12
|
+
|
|
13
|
+
| 验收 ID | 归属 |
|
|
14
|
+
|---------|------|
|
|
15
|
+
| A1/A2(路径纯函数 + 钉路径) | Task 1 |
|
|
16
|
+
| A3(update_note 自愈 + 重钉 pin) | Task 1 |
|
|
17
|
+
| A9(护栏:改名/移目录保持绿) | Task 1 |
|
|
18
|
+
| A4–A8(CLI 症状层) | Task 2 |
|
|
19
|
+
| CHANGELOG/注释交付物 | Task 3 |
|
|
20
|
+
| U1(真实库受控演练) | 合并后单独人工项,非代码任务 |
|
|
21
|
+
|
|
22
|
+
## 前置事实(实现者必读,违反即返工)
|
|
23
|
+
|
|
24
|
+
1. **原子性约束**:`models.py` 的钉路径与 `note.py` 的 `update_note` 调整**必须同任务同 commit 落地**。中间态(只钉路径)会让 `tests/unit/test_edit.py:72-82`(改标题→改名)与 `:373-390`(改 type→跨目录)挂掉(E2b 已实证)。
|
|
25
|
+
2. **契约边界**:改 title/topic/type 的意图只走 `update_note`;`save_note` 是就地写。不要给 `save_note` 加任何「找旧文件/改名」逻辑——本修复不靠它。
|
|
26
|
+
3. **改动面**:只允许碰 `jfox/models.py`、`jfox/note.py`、两个测试文件、`CHANGELOG.md`。不碰 `moc/`、`redirect.py`、其余 save_note 站点(钉路径后它们自动受益)。
|
|
27
|
+
4. **测试命名**:文件/用例名避开子串 `search|semantic|embedding|vector|query|suggest`;用例标题全局唯一(#483 闸门)。本计划的文件名已满足:`test_note_path_rules.py`、`test_stale_name_single_file.py`。
|
|
28
|
+
5. **夹具手法**:发散名 = 只 rename 文件(保留 id 前缀),不改 frontmatter——`cli_fast` 每条命令独立子进程,索引按盘重建,发散笔记可被正常发现/解析。
|
|
29
|
+
6. **#483 之后 add permanent 有 dedup 闸门**:测试标题彼此差异要大,别用近似标题。
|
|
30
|
+
|
|
31
|
+
---
|
|
32
|
+
|
|
33
|
+
## Task 1: 路径语义核心(models.py + note.py + A1/A2/A3/A9)
|
|
34
|
+
|
|
35
|
+
**Files:**
|
|
36
|
+
|
|
37
|
+
- Create: `tests/unit/test_note_path_rules.py`
|
|
38
|
+
- Create: `tests/integration/test_stale_name_single_file.py`(先只放 A3 两个用例)
|
|
39
|
+
- Modify: `jfox/models.py`
|
|
40
|
+
- Modify: `jfox/note.py`
|
|
41
|
+
|
|
42
|
+
- [ ] **Step 1: 写失败测试**
|
|
43
|
+
|
|
44
|
+
`tests/unit/test_note_path_rules.py`(A1/A2,纯函数层,无需 KB 写入):
|
|
45
|
+
|
|
46
|
+
```python
|
|
47
|
+
class TestExpectedFilepath:
|
|
48
|
+
def test_permanent_uses_title_slug(self) # A1:expected == config.notes_dir/"permanent"/filename
|
|
49
|
+
def test_session_uses_topic(self) # A1
|
|
50
|
+
def test_session_without_topic_falls_back_to_title(self) # A1
|
|
51
|
+
def test_fleeting_uses_dash_form(self) # A1:{id[:8]}-{id[8:]}.md
|
|
52
|
+
def test_expected_filepath_ignores_pin(self) # A1:set_filepath 后 expected 不变、filepath 变
|
|
53
|
+
|
|
54
|
+
class TestFromMarkdownPin:
|
|
55
|
+
def test_with_filepath_pins_it(self) # A2:note.filepath == 传入路径
|
|
56
|
+
def test_without_filepath_stays_unpinned(self) # A2:note.filepath == expected_filepath
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
`tests/integration/test_stale_name_single_file.py`(A3,先行两个用例;发散名 helper 共用):
|
|
60
|
+
|
|
61
|
+
```python
|
|
62
|
+
def _diverge(filepath: str) -> Path:
|
|
63
|
+
"""只 rename 文件制造发散名(保留 id 前缀),模拟外部改标题未同步改名"""
|
|
64
|
+
p = Path(filepath)
|
|
65
|
+
d = p.with_name(f"{p.stem}-diverged.md")
|
|
66
|
+
p.rename(d)
|
|
67
|
+
return d
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def _files_for(dir_path: Path, nid: str) -> list:
|
|
71
|
+
return sorted(dir_path.glob(f"{nid}*.md"))
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
class TestUpdateSelfHeal:
|
|
75
|
+
def test_edit_title_on_diverged_name_canonicalizes(self, cli_fast):
|
|
76
|
+
"""A3:发散名笔记经 edit --title 后规范化为规则名,全程单文件"""
|
|
77
|
+
# add permanent → 从 add 的 JSON 取 note.filepath → _diverge →
|
|
78
|
+
# cli_fast.edit(id, title="全新标题X") →
|
|
79
|
+
# 断言 _files_for 单文件、文件名含新标题 slug、旧发散名不存在
|
|
80
|
+
|
|
81
|
+
def test_second_save_after_update_no_resurrect(self, temp_kb_registered):
|
|
82
|
+
"""A3:同对象 update_note 后再 save_note,不复活旧路径(重钉 pin 契约)"""
|
|
83
|
+
# 复用 test_delete_backlink_cleanup.py 的 temp_kb_registered + use_kb 模式:
|
|
84
|
+
# load → note.update_note(改标题)→ 对同对象 note.save_note →
|
|
85
|
+
# 断言旧路径文件未被重新创建,且规则名文件唯一
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
(`temp_kb_registered` 参照 `tests/unit/test_delete_backlink_cleanup.py` 的用法;若该 fixture 名不匹配,用 conftest 中已有的等价临时 KB 上下文。)
|
|
89
|
+
|
|
90
|
+
- [ ] **Step 2: 运行确认失败**
|
|
91
|
+
|
|
92
|
+
```bash
|
|
93
|
+
uv run pytest tests/unit/test_note_path_rules.py tests/integration/test_stale_name_single_file.py -x -q
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
预期:`expected_filepath` 不存在(AttributeError)/ 钉路径未生效 → FAIL。只确认 RED,不修。
|
|
97
|
+
|
|
98
|
+
- [ ] **Step 3: 实现 `jfox/models.py`**
|
|
99
|
+
|
|
100
|
+
① 新增 property(放 `filepath` 旁,注释为交付物):
|
|
101
|
+
|
|
102
|
+
```python
|
|
103
|
+
@property
|
|
104
|
+
def expected_filepath(self) -> Path:
|
|
105
|
+
"""按当前字段现算的规则路径(type 目录 + filename),不看 _filepath pin。
|
|
106
|
+
|
|
107
|
+
#549 两分法的另一半:update_note(规范化写)以本属性为写盘目标;
|
|
108
|
+
filepath 在笔记从磁盘加载时被钉住为真实路径(就地写)。
|
|
109
|
+
用 note_obj.filepath 代替本属性的已知后果:钉路径后改名判据
|
|
110
|
+
`old != note.filepath` 恒假,edit --title / 改 type 不再改名移动
|
|
111
|
+
(tests/unit/test_edit.py:72-82、:373-390 必挂)。
|
|
112
|
+
"""
|
|
113
|
+
from .config import config
|
|
114
|
+
|
|
115
|
+
return config.notes_dir / self.type.value / self.filename
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
② `from_markdown`:把 `return cls(...)` 改为先存变量再钉路径(注释为交付物):
|
|
119
|
+
|
|
120
|
+
```python
|
|
121
|
+
note = cls(
|
|
122
|
+
... # 现有参数不动
|
|
123
|
+
)
|
|
124
|
+
# #549:钉住加载来源的真实磁盘路径。此后 note.filepath 返回真实路径,
|
|
125
|
+
# save_note(就地写)写回原文件——文件名与当前字段分家时也不会在规则名上
|
|
126
|
+
# 另写同 id 双文件。需要「按当前字段重新定址」的操作(改标题/改 type)
|
|
127
|
+
# 请走 note.update_note,其写盘目标用 expected_filepath。
|
|
128
|
+
if filepath is not None:
|
|
129
|
+
note._filepath = Path(filepath)
|
|
130
|
+
return note
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
③ `filepath` property 的 docstring 补一句「加载的笔记此处返回真实磁盘路径;规则路径请用 expected_filepath」。
|
|
134
|
+
|
|
135
|
+
- [ ] **Step 4: 实现 `jfox/note.py`**
|
|
136
|
+
|
|
137
|
+
① `update_note` 写盘段(原 `_atomic_write(note_obj.filepath, ...)` 处):
|
|
138
|
+
|
|
139
|
+
```python
|
|
140
|
+
# 规范化写(#549):目标路径按当前字段现算,不看加载 pin。
|
|
141
|
+
target = note_obj.expected_filepath
|
|
142
|
+
_atomic_write(target, note_obj.to_markdown())
|
|
143
|
+
|
|
144
|
+
if old_filepath != target and old_filepath.exists():
|
|
145
|
+
old_filepath.unlink()
|
|
146
|
+
logger.info(f"Renamed note file: {old_filepath} -> {target}")
|
|
147
|
+
|
|
148
|
+
# #549:写盘成功后把 pin 重钉到刚写入的路径——否则同一对象随后的
|
|
149
|
+
# save_note(就地写)会按陈旧 pin 写回旧路径、复活同 id 双文件。
|
|
150
|
+
note_obj.set_filepath(target)
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
② `save_note` docstring 补契约边界(交付物):
|
|
154
|
+
|
|
155
|
+
```
|
|
156
|
+
注意(#549):写盘目标是 note.filepath——对从磁盘加载的笔记即「就地写回
|
|
157
|
+
加载路径」;就地写不保证文件名随字段变化。改 title/topic/type 的意图请用
|
|
158
|
+
update_note(规范化写:按当前字段现算目标并删除旧文件)。
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
- [ ] **Step 5: 运行确认通过**
|
|
162
|
+
|
|
163
|
+
```bash
|
|
164
|
+
uv run pytest tests/unit/test_note_path_rules.py tests/integration/test_stale_name_single_file.py -q
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
预期全绿。
|
|
168
|
+
|
|
169
|
+
- [ ] **Step 6: 护栏回归(A9)**
|
|
170
|
+
|
|
171
|
+
```bash
|
|
172
|
+
uv run pytest tests/unit/test_edit.py -q
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
预期全绿(改标题改名、改 type 跨目录不受影响)。
|
|
176
|
+
|
|
177
|
+
- [ ] **Step 7: 快速回归**
|
|
178
|
+
|
|
179
|
+
```bash
|
|
180
|
+
uv run pytest tests/unit -m "not embedding and not slow" -q
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
- [ ] **Step 8: commit**
|
|
184
|
+
|
|
185
|
+
```bash
|
|
186
|
+
git add jfox/models.py jfox/note.py tests/unit/test_note_path_rules.py tests/integration/test_stale_name_single_file.py
|
|
187
|
+
git commit -m "fix(note): 加载笔记钉住真实路径,update_note 规范化写+重钉 pin——消除同 ID 双文件(#549 A1-A3/A9)"
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
---
|
|
191
|
+
|
|
192
|
+
## Task 2: CLI 症状层集成测试(A4–A8)
|
|
193
|
+
|
|
194
|
+
**Files:**
|
|
195
|
+
|
|
196
|
+
- Modify: `tests/integration/test_stale_name_single_file.py`
|
|
197
|
+
|
|
198
|
+
- [ ] **Step 1: 补五个测试类(TDD:先写先跑,应在 Task 1 的实现上直接转绿——若红说明 Task 1 有漏)**
|
|
199
|
+
|
|
200
|
+
```python
|
|
201
|
+
class TestAddBackfill:
|
|
202
|
+
def test_add_link_to_diverged_target_keeps_single_file(self, cli_fast):
|
|
203
|
+
"""A4:add 链接发散名目标 → 单文件 + backlinks 落真实文件"""
|
|
204
|
+
# A = add permanent(标题「回填目标甲」)→ _diverge →
|
|
205
|
+
# B = add「链向 [[回填目标甲]]」→
|
|
206
|
+
# 断言 _files_for(A) 单文件(发散名那份)、A 文件 backlinks 含 B.id
|
|
207
|
+
|
|
208
|
+
class TestEditBackfill:
|
|
209
|
+
def test_edit_add_link_diverged_target_keeps_single_file(self, cli_fast):
|
|
210
|
+
"""A5:edit 新增链接 → 单文件 + backlinks 落位"""
|
|
211
|
+
def test_edit_remove_link_diverged_target_keeps_single_file(self, cli_fast):
|
|
212
|
+
"""A5:edit 移除链接 → backlinks 移除 + 仍单文件"""
|
|
213
|
+
|
|
214
|
+
class TestRebuildBacklinks:
|
|
215
|
+
def test_rebuild_touches_diverged_note_in_place(self, cli_fast):
|
|
216
|
+
"""A6:rebuild --backlinks 触及发散名笔记时单文件"""
|
|
217
|
+
# 造 links/backlinks 必变化:直接改发散文件 frontmatter,给 backlinks 加一个假 id
|
|
218
|
+
# cli_fast.index_rebuild(backlinks=True) →
|
|
219
|
+
# 断言单文件、backlinks 被重算(假 id 消失)
|
|
220
|
+
|
|
221
|
+
class TestShowDelete:
|
|
222
|
+
def test_show_reads_diverged_note(self, cli_fast):
|
|
223
|
+
"""A7:show 发散名笔记 success=true(此前报 Errno 2)"""
|
|
224
|
+
def test_delete_removes_real_file(self, cli_fast):
|
|
225
|
+
"""A7:delete --force 删真实文件(无入链前置)→ glob 为空"""
|
|
226
|
+
|
|
227
|
+
class TestMocMemberBackfill:
|
|
228
|
+
def test_moc_backfill_diverged_member_keeps_single_file(self, temp_kb_registered):
|
|
229
|
+
"""A8:moc 成员回填(python 级 backfill_moc_backlinks)→ 单文件"""
|
|
230
|
+
# 构造 structure Note(或 CLI add --type structure)+ 发散名成员
|
|
231
|
+
# 调 jfox.moc.generate.backfill_moc_backlinks(moc_note, [member_id], cfg)
|
|
232
|
+
# 断言单文件 + backlinks 含 moc.id
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
- [ ] **Step 2: 运行该文件全量**
|
|
236
|
+
|
|
237
|
+
```bash
|
|
238
|
+
uv run pytest tests/integration/test_stale_name_single_file.py -q
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
- [ ] **Step 3: 相关既有集成测试回归**
|
|
242
|
+
|
|
243
|
+
```bash
|
|
244
|
+
uv run pytest tests/integration/test_links_edit_add.py tests/integration/test_index_rebuild_backlinks.py tests/integration/test_edit_roundtrip.py tests/unit/test_delete_backlink_cleanup.py tests/unit/test_note_promote.py -q
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
预期全绿(同族回填/晋升链路未受影响)。
|
|
248
|
+
|
|
249
|
+
- [ ] **Step 4: commit**
|
|
250
|
+
|
|
251
|
+
```bash
|
|
252
|
+
git add tests/integration/test_stale_name_single_file.py
|
|
253
|
+
git commit -m "test(links): 发散名笔记 CLI 症状层集成测试——add/edit/rebuild/show+delete/moc 均单文件(#549 A4-A8)"
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
---
|
|
257
|
+
|
|
258
|
+
## Task 3: CHANGELOG + 注释交付物复核 + 全量回归
|
|
259
|
+
|
|
260
|
+
**Files:**
|
|
261
|
+
|
|
262
|
+
- Modify: `CHANGELOG.md`
|
|
263
|
+
|
|
264
|
+
- [ ] **Step 1: `## [Unreleased]` 的 `### Fixed` 下加条目**
|
|
265
|
+
|
|
266
|
+
```markdown
|
|
267
|
+
- links: 加载的笔记钉住真实磁盘路径——文件名与当前字段分家时,补 backlink / rebuild --backlinks / MOC 成员回填不再写出同 ID 双文件,`show`/`delete` 也能正确读写该笔记;`update_note` 改为显式规范化写并在成功后重钉路径(#549)
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
- [ ] **Step 2: 注释交付物复核(对照 spec §7)**:`filepath` property、`from_markdown`、`update_note` 三处注释齐(两分法、E2b 后果、重钉原因);不齐则补。
|
|
271
|
+
|
|
272
|
+
- [ ] **Step 3: markdownlint**
|
|
273
|
+
|
|
274
|
+
```bash
|
|
275
|
+
npx --yes markdownlint-cli2@0.23.2 "CHANGELOG.md"
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
- [ ] **Step 4: 全量快速回归(本地 CR 前置)**
|
|
279
|
+
|
|
280
|
+
```bash
|
|
281
|
+
uv run pytest tests/ -m "not embedding and not slow" -q
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
- [ ] **Step 5: commit**
|
|
285
|
+
|
|
286
|
+
```bash
|
|
287
|
+
git add CHANGELOG.md
|
|
288
|
+
git commit -m "docs(changelog): #549 同 ID 双文件修复条目"
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
---
|
|
292
|
+
|
|
293
|
+
## 完成标准
|
|
294
|
+
|
|
295
|
+
- A1–A9 全部有对应测试且全绿;U1 留待合并后用户点头执行(spec §5)。
|
|
296
|
+
- 快速套件 `tests/ -m "not embedding and not slow"` 全绿。
|
|
297
|
+
- 分支只含 4 个 commit(spec / 核心修复 / 症状层测试 / changelog),主 checkout 零改动。
|
|
298
|
+
|
|
299
|
+
---
|
|
300
|
+
|
|
301
|
+
## 执行偏差记录(终审补记)
|
|
302
|
+
|
|
303
|
+
1. **测试文件名更名**:spec 中的 `tests/unit/test_note_path_semantics.py` 实际落地为 `test_note_path_rules.py`——#483 命名约束要求避开子串 `semantic`(spec 已同步修订)。
|
|
304
|
+
2. **分支为 6 个 commit 而非 4 个**:Task 1 的 lint fix wave(ruff/black)amend 进核心 commit;第 5 个 commit `a5ed229` 为终审挂账的注释债清理(`redirect.py`、`moc/cli.py`、`test_moc_member_commands.py` 纯注释/docstring——reviewer 指出「from_markdown 不回填 `_filepath`」在修复后已成假命题),第 6 个为本修订。前置事实 3 的「不碰 moc/、redirect.py」原指行为代码,注释债清理经 reviewer 建议纳入。
|
|
305
|
+
3. **plan 的用例骨架微调**:`temp_kb_registered` 实为 `tests/utils/temp_kb.py` 的 context manager(非 fixture),A3-2/A8 按计划备注改用 `use_kb(kb_name)` + python 级 note API 模式。
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
# #549 spec:加载笔记按真实磁盘路径读写(消除同 ID 双文件)
|
|
2
|
+
|
|
3
|
+
- 状态:**confirmed v2(用户已批准并实现完成;整分支终审 Ready to merge: Yes)**
|
|
4
|
+
- v2 变更(review 修订):① U1 重设计为受控演练(v1 的「incident 复刻」在笔记已规范化后验证不了任何东西);② 补 `update_note` 成功后重钉 pin 契约;③ 补 `save_note` 就地写边界(改 title/topic/type 须走 update_note);④ A7 补无入链前置;⑤ A3 定为 integration;⑥ 防回归注释列为交付物。
|
|
5
|
+
- issue:zhuxixi/jfox#549
|
|
6
|
+
- 调研依据:`research/root-cause-and-fix-verification.md`(含原型实验 E1–E3)
|
|
7
|
+
|
|
8
|
+
## 1. 背景与现状
|
|
9
|
+
|
|
10
|
+
一个 bug 类:**「笔记磁盘文件名 ≠ 按当前字段现算的规则名」时,jfox 的读写路径会指向错误位置**。写面产生同 ID 双文件,读/删面直接失败。
|
|
11
|
+
|
|
12
|
+
- 触发预置条件:磁盘名 ≠ 规则派生名(外部改标题未同步改名 / legacy slug 漂移 / KB 迁移)。
|
|
13
|
+
- 当前知识库实测:**0 条**处于触发态(issue 里的双文件已被清理,`index verify` duplicate_ids 为空)。真实库今天实弹命中过一条永久笔记(incident)。
|
|
14
|
+
- 根因链(HEAD 核实):`models.py` `filename` 三分支 → `from_markdown` 丢弃 filepath(`_filepath` 恒 None)→ `filepath` property 现算 → `save_note` 无旧文件比对。
|
|
15
|
+
- 站点图(8 处,见调研文件 §4):写面 4 处(`add` 回填、`edit` 回填、`rebuild --backlinks`、MOC 成员回填),读/删面 4 处(`show`、`delete`、MOC ghost 检查、`list --paths`)。
|
|
16
|
+
- 站点图复核(v2,已逐个读码):`prompts/judge.py:130`、`prompts/actions.py:252`、`auto_summary/runner.py:651`、`moc/generate.py:79` 四个 `save_note` 站点均构造全新对象(无加载路径),不属本类。
|
|
17
|
+
- 仓库已有同类修复范式(#392 A2/A3 → #422):delete/promote 的 target 清理用「`find_note_file` 真实路径 + 重读 fresh + `_atomic_write(actual_path)`」。
|
|
18
|
+
|
|
19
|
+
## 2. 目标与非目标
|
|
20
|
+
|
|
21
|
+
**目标(三个症状一次消掉)**
|
|
22
|
+
|
|
23
|
+
1. 写面:对 stale 名笔记的任何写入**就地落在真实文件**,不再产生双文件。
|
|
24
|
+
2. 读面:`show` 能读 stale 名笔记;`list --paths` 输出真实路径;MOC 成员检查不再误判 ghost。
|
|
25
|
+
3. 删面:`delete` 能删掉 stale 名笔记的真实文件。
|
|
26
|
+
4. 行为不变:`edit --title` 改名、改 type 跨目录移动等既有「规范化写」语义**原样保留**。
|
|
27
|
+
|
|
28
|
+
**非目标**
|
|
29
|
+
|
|
30
|
+
- 存量双文件修复(当前 0 条;如需另行处理)。
|
|
31
|
+
- 对已有 stale 名笔记做批量规范化改名(无必要,就地读写即可;update_note 触碰时会自然自愈)。
|
|
32
|
+
- 清理 `moc/cli.py:589`、`redirect.py:420` 的既有绕法(保留,注释已记录根因;本次不动以缩小 diff)。
|
|
33
|
+
- 并发丢更新(#392 A3 范畴,本修复只解决路径正确性,不改变各站点的并发语义)。
|
|
34
|
+
- legacy 文件名的自动迁移(#407/#408 已按读面对账解决)。
|
|
35
|
+
|
|
36
|
+
## 3. 设计
|
|
37
|
+
|
|
38
|
+
### 3.1 核心决策:路径语义两分法
|
|
39
|
+
|
|
40
|
+
| 语义 | 方法 | 写盘目标 | 用途 |
|
|
41
|
+
|------|------|----------|------|
|
|
42
|
+
| **就地写** | `save_note` | `note.filepath`(加载时钉住的真实路径) | backlink 回填、rebuild、MOC 成员回填 |
|
|
43
|
+
| **规范化写** | `update_note` | **规则派生路径**(按当前 type+title/topic 现算)+ 删旧文件 | `edit`、promote/archive/reject、MOC 保存 |
|
|
44
|
+
|
|
45
|
+
### 3.2 决策表
|
|
46
|
+
|
|
47
|
+
| 场景 | 期望行为 | 机制 |
|
|
48
|
+
|------|----------|------|
|
|
49
|
+
| `from_markdown(content, filepath)` | `filepath` 属性 === 传入的真实路径 | 构造后钉 `_filepath` |
|
|
50
|
+
| `from_markdown(content)`(无 filepath) | `filepath` = 规则派生(现状) | pin 保持 None |
|
|
51
|
+
| `save_note`(回填/重建) | 就地写真实文件,无新文件 | filepath 已钉 |
|
|
52
|
+
| `update_note`(edit/promote/archive/moc save) | 写规则派生名;旧文件不存在则不动;不同则删旧 | 目标路径改用 `expected_filepath` |
|
|
53
|
+
| stale 名笔记被 `update_note` 触碰 | 规范化为规则名(自愈,单文件) | 同上 |
|
|
54
|
+
| `delete_note` | 删真实文件 | filepath 已钉(**无需改代码**) |
|
|
55
|
+
| `show` / `list --paths` | 读/输出真实路径 | filepath 已钉(**无需改代码**) |
|
|
56
|
+
| MOC 成员回填(`moc/generate.py:109/140`) | 就地写真实文件 | filepath 已钉(**无需改代码**) |
|
|
57
|
+
| MOC ghost 检查(`moc/generate.py:51`) | stale 名成员不再误判 ghost | filepath 已钉(**无需改代码**) |
|
|
58
|
+
|
|
59
|
+
### 3.3 组件契约
|
|
60
|
+
|
|
61
|
+
- `Note.filename -> str`(既有,纯函数):三分支规则名。
|
|
62
|
+
- `Note.expected_filepath -> Path`(**新增,纯**):`config.notes_dir / type.value / filename`——规则派生路径,永不看 pin。
|
|
63
|
+
- `Note.filepath -> Path`(调整 docstring):pin 优先,否则 `expected_filepath`。
|
|
64
|
+
- `Note.from_markdown(content, filepath=None) -> Note`(调整):filepath 非 None 时钉 `_filepath`(coerce 为 `Path`)。
|
|
65
|
+
- `note.save_note(note, add_to_index=True) -> bool`(docstring 明确就地写语义,逻辑不变)。**契约边界**:就地写不保证文件名随字段变——**改 title/topic/type 的意图必须走 `update_note`**;对加载的笔记 `save_note` 只会写回加载路径。
|
|
66
|
+
- `note.update_note(note_obj, add_to_index=True) -> bool`(调整):写盘与改名判据改用 `note_obj.expected_filepath`;`find_note_file` 找旧路径与删旧逻辑不变;docstring 补「规范化写」语义。**写盘成功后必须把 `note_obj._filepath` 重钉为刚写入的路径**——否则同对象随后的 `save_note` 会按陈旧 pin 写回旧路径、复活双文件(v1 遗漏;原型用 finally 恢复旧 pin 掩盖了此点)。
|
|
67
|
+
|
|
68
|
+
### 3.4 数据流
|
|
69
|
+
|
|
70
|
+
```
|
|
71
|
+
磁盘文件 --load_note--> from_markdown(content, path)
|
|
72
|
+
└─ 钉 _filepath = path
|
|
73
|
+
save_note(note) → _atomic_write(note.filepath) # 就地
|
|
74
|
+
update_note(note) → _atomic_write(note.expected_filepath) # 规范化
|
|
75
|
+
+ unlink(find_note_file(id)) if 不同且存在
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## 4. 可测性拆分设计
|
|
79
|
+
|
|
80
|
+
- **纯函数层(unit,无需 IO/KB)**:`Note.expected_filepath` 三分支(fleeting / session(topic 优先,空 topic 回退 title) / 其余);`Note.filepath` 的 pin/非 pin 两态;`from_markdown` 有/无 filepath 的钉与不钉。测试文件 `tests/unit/test_note_path_rules.py`。
|
|
81
|
+
- **IO 层(integration,temp KB + mock embedding)**:CLI 级复现矩阵(下表 A4–A8)。夹具手法复用 `tests/unit/test_delete_backlink_cleanup.py:655-695`(`os.rename` 到 `{id}-renamed.md` 制造发散名),不新增抽象层。
|
|
82
|
+
- **护栏(回归)**:既有 `tests/unit/test_edit.py:72-82`(改标题→改名)、`:373-390`(改 type→跨目录)必须保持绿。
|
|
83
|
+
- 命名约束:测试文件/用例名避开子串 `search|semantic|embedding|vector|query|suggest`(`-m "not embedding"` 过滤口径);用例标题全局唯一(#483 闸门)。
|
|
84
|
+
- 副作用隔离:不改 `models.py` 的 IO 面(models 保持无 IO);`note.py` 保持「IO + 路径语义」,不引入新模块。
|
|
85
|
+
|
|
86
|
+
## 5. 验收矩阵
|
|
87
|
+
|
|
88
|
+
| ID | 功能点 | 验收方式 | 具体验证 | 通过标准 |
|
|
89
|
+
|----|--------|----------|----------|----------|
|
|
90
|
+
| A1 | `expected_filepath`/`filename` 三分支与 pin 两态 | 自动化(unit) | `uv run pytest tests/unit/test_note_path_rules.py -m "not embedding"` | 全绿 |
|
|
91
|
+
| A2 | `from_markdown` 钉/不钉 | 自动化(unit) | 同上 | 全绿 |
|
|
92
|
+
| A3 | `update_note` 写规则名 + 删旧(stale 名自愈,单文件)+ 成功后重钉 pin | 自动化(integration) | `uv run pytest tests/integration/test_stale_name_single_file.py`(含自愈用例与「同对象 update 后再 save_note 不复活旧文件」用例) | 全绿,单文件 |
|
|
93
|
+
| A4 | `add` 回填 stale 名笔记:无新文件、backlinks 落真实文件 | 自动化(integration) | `uv run pytest tests/integration/test_stale_name_single_file.py` | 断言 `glob(id*)` 唯一 + backlinks 含链接者 |
|
|
94
|
+
| A5 | `edit` 增删链接(incident 场景) | 自动化(integration) | 同文件 | 单文件 + backlinks 正确 |
|
|
95
|
+
| A6 | `index rebuild --backlinks` 触及 stale 名笔记 | 自动化(integration) | 同文件 | 单文件(逐条) |
|
|
96
|
+
| A7 | `show` 可读 + `delete --force` 删真实文件 | 自动化(integration) | 同文件 | `success=true`;文件消失且无残留。**前置:被删笔记必须无入链**(否则触发引用保护 `1 note(s) reference this note`,E3c 实测) |
|
|
97
|
+
| A8 | MOC 成员回填/ghost 检查 | 自动化(integration) | 同文件或 `tests/unit/test_moc_member_cli.py` 增补 | 单文件;成员被接受 |
|
|
98
|
+
| A9 | 护栏:改标题改名 / 改 type 跨目录 | 自动化(integration) | `uv run pytest tests/unit/test_edit.py` | 全绿 |
|
|
99
|
+
| U1 | 真实库受控演练(v2 重设计) | 用户实测(须用户点头后由 agent 用仓库版代码执行) | ① `jfox add` 在真实库建牺牲笔记 S;② `mv` 其文件制造发散名;③ `jfox add` 一条链接 S 的笔记触发回填;④ 断言 `glob(S.id + "*")` 单文件且 backlinks 落位;⑤ `jfox delete` 清理 S 与链接者;⑥ `jfox index verify --json` 的 `duplicate_ids` 为空 | 六步全部符合,现场无残留 |
|
|
100
|
+
|
|
101
|
+
U1 说明(v2):v1 的「incident 复刻」无效——incident 笔记已被清理成规范名,普通回填测不到 stale 路径;真实库当前 0 条 stale 笔记,要验证只能受控制造。上列六步全程可逆(牺牲笔记 + 完整清理),执行前需用户确认;若倾向零触碰,可降级为只读核验(仅⑥)。未执行前不允许宣称全部验收完成。
|
|
102
|
+
|
|
103
|
+
## 6. 风险与缓解
|
|
104
|
+
|
|
105
|
+
| 风险 | 缓解 |
|
|
106
|
+
|------|------|
|
|
107
|
+
| 钉路径让 `update_note` 改名失效(E2b 已实证) | 本 spec 的 A3/A9 覆盖:update_note 改 `expected_filepath` 后必须同时过 stale 自愈与新改名两测 |
|
|
108
|
+
| MOC 标题编辑后文件随 update_note 改名(行为变化) | 现库 0 条 stale MOC,且属规范化收敛;A8 覆盖成员侧,MOC 自身由 A9 同族语义覆盖 |
|
|
109
|
+
| `from_markdown` 公共面被误用 | 调用方仅 `note.py` 内部 2 处 + 测试(已 grep 核实) |
|
|
110
|
+
| 其他隐藏站点 | 钉路径是「根因级」修复,未来新站点天然免疫;新增测试锁定三个症状 |
|
|
111
|
+
| load 之后文件被外部改名(微竞态) | save 会按陈旧 pin 在旧路径复活文件。与现状同窗口(现状更糟:直接写规则名),非本次范围;`update_note` 重钉 pin 已收窄此窗口 |
|
|
112
|
+
|
|
113
|
+
## 7. 实现文件清单(预期)
|
|
114
|
+
|
|
115
|
+
- `jfox/models.py`:`from_markdown` 钉路径;新增 `expected_filepath` property;`filepath` docstring。
|
|
116
|
+
- `jfox/note.py`:`update_note` 写盘/改名判据改用 `expected_filepath`(+docstring);`save_note` docstring 明确就地写。
|
|
117
|
+
- **防回归注释(交付物,#548 教训)**:`filepath` property、`from_markdown`、`update_note` 三处必须留注释,说明「就地写 / 规范化写」两分法、update_note 为何不能用 `note_obj.filepath`(E2b:钉路径后改名判据恒假,`test_edit.py:72-82` 必挂)、以及写盘成功后重钉 pin 的原因——防止未来被「简化」回 `note_obj.filepath`。
|
|
118
|
+
- `tests/unit/test_note_path_rules.py`(新)。
|
|
119
|
+
- `tests/integration/test_stale_name_single_file.py`(新,含 A4–A8)。
|
|
120
|
+
- `CHANGELOG.md`:`## [Unreleased]` 加 Fixes 条目。
|
|
121
|
+
- 视情况:`tests/unit/test_edit.py` 增补 stale 自愈用例。
|
|
@@ -583,9 +583,9 @@ def _exact_load(idx: "NoteIndex", note_id: str) -> Optional[Note]:
|
|
|
583
583
|
note = load_note(Path(meta.filepath))
|
|
584
584
|
if note is None or note.id != note_id:
|
|
585
585
|
return None
|
|
586
|
-
#
|
|
587
|
-
#
|
|
588
|
-
#
|
|
586
|
+
# #549 起 from_markdown 已钉住加载路径,此 set_filepath 冗余但保留:
|
|
587
|
+
# 它同时是「索引命中的路径必须真实存在」的防御(防 #407/#408 幽灵误判回归,
|
|
588
|
+
# 见 tests/integration/test_moc_member_commands.py 的 legacy 用例)。
|
|
589
589
|
note.set_filepath(Path(meta.filepath))
|
|
590
590
|
if not note.filepath.exists():
|
|
591
591
|
return None
|
|
@@ -90,10 +90,14 @@ class Note:
|
|
|
90
90
|
embedding: Optional[List[float]] = None # 向量
|
|
91
91
|
score: Optional[float] = None # 检索得分
|
|
92
92
|
hop: Optional[int] = None # 图谱距离
|
|
93
|
-
_filepath: Optional[Path] = None #
|
|
93
|
+
_filepath: Optional[Path] = None # 真实磁盘路径 pin(#549:加载时钉住;update_note 成功后重钉)
|
|
94
94
|
|
|
95
95
|
def set_filepath(self, path: Path):
|
|
96
|
-
"""
|
|
96
|
+
"""钉住笔记的真实文件路径。
|
|
97
|
+
|
|
98
|
+
#549 起承担生产职责:from_markdown 加载时钉住来源路径(save_note 就地写);
|
|
99
|
+
update_note 规范化写成功后重钉到新路径(防陈旧 pin 复活同 ID 双文件)。
|
|
100
|
+
"""
|
|
97
101
|
self._filepath = path
|
|
98
102
|
|
|
99
103
|
@property
|
|
@@ -112,11 +116,27 @@ class Note:
|
|
|
112
116
|
|
|
113
117
|
@property
|
|
114
118
|
def filepath(self) -> Path:
|
|
115
|
-
"""
|
|
119
|
+
"""完整文件路径。
|
|
120
|
+
|
|
121
|
+
#549:从磁盘加载的笔记此处返回钉住的真实磁盘路径(可能偏离规则名);
|
|
122
|
+
按当前字段现算的规则路径请用 expected_filepath。
|
|
123
|
+
"""
|
|
116
124
|
# 如果设置了自定义路径,优先使用
|
|
117
125
|
if self._filepath is not None:
|
|
118
126
|
return self._filepath
|
|
119
127
|
|
|
128
|
+
return self.expected_filepath
|
|
129
|
+
|
|
130
|
+
@property
|
|
131
|
+
def expected_filepath(self) -> Path:
|
|
132
|
+
"""按当前字段现算的规则路径(type 目录 + filename),不看 _filepath pin。
|
|
133
|
+
|
|
134
|
+
#549 两分法的另一半:update_note(规范化写)以本属性为写盘目标;
|
|
135
|
+
filepath 在笔记从磁盘加载时被钉住为真实路径(就地写)。
|
|
136
|
+
用 note_obj.filepath 代替本属性的已知后果:钉路径后改名判据
|
|
137
|
+
`old != note.filepath` 恒假,edit --title / 改 type 不再改名移动
|
|
138
|
+
(tests/unit/test_edit.py:72-82、:373-390 必挂)。
|
|
139
|
+
"""
|
|
120
140
|
from .config import config
|
|
121
141
|
|
|
122
142
|
base = config.notes_dir / self.type.value
|
|
@@ -196,7 +216,8 @@ class Note:
|
|
|
196
216
|
else:
|
|
197
217
|
updated = datetime.fromisoformat(updated_str)
|
|
198
218
|
|
|
199
|
-
|
|
219
|
+
# 先构造再钉路径(见函数尾部 #549 注释)
|
|
220
|
+
note = cls(
|
|
200
221
|
id=fm.get("id", ""),
|
|
201
222
|
title=fm.get("title", title),
|
|
202
223
|
content=content_text.strip(),
|
|
@@ -222,6 +243,13 @@ class Note:
|
|
|
222
243
|
knowledge_type=fm.get("knowledge_type"),
|
|
223
244
|
status=fm.get("status"),
|
|
224
245
|
)
|
|
246
|
+
# #549:钉住加载来源的真实磁盘路径。此后 note.filepath 返回真实路径,
|
|
247
|
+
# save_note(就地写)写回原文件——文件名与当前字段分家时也不会在规则名上
|
|
248
|
+
# 另写同 id 双文件。需要「按当前字段重新定址」的操作(改标题/改 type)
|
|
249
|
+
# 请走 note.update_note,其写盘目标用 expected_filepath。
|
|
250
|
+
if filepath is not None:
|
|
251
|
+
note._filepath = Path(filepath)
|
|
252
|
+
return note
|
|
225
253
|
|
|
226
254
|
def to_dict(self) -> Dict[str, Any]:
|
|
227
255
|
"""转换为字典(用于 JSON 输出)"""
|
|
@@ -149,7 +149,12 @@ def _atomic_write(filepath: Path, content: str) -> None:
|
|
|
149
149
|
|
|
150
150
|
|
|
151
151
|
def save_note(note: Note, add_to_index: bool = True) -> bool:
|
|
152
|
-
"""
|
|
152
|
+
"""保存笔记到文件(就地写)。
|
|
153
|
+
|
|
154
|
+
注意(#549):写盘目标是 note.filepath——对从磁盘加载的笔记即「就地写回
|
|
155
|
+
加载路径」;就地写不保证文件名随字段变化。改 title/topic/type 的意图请用
|
|
156
|
+
update_note(规范化写:按当前字段现算目标并删除旧文件)。
|
|
157
|
+
"""
|
|
153
158
|
try:
|
|
154
159
|
_atomic_write(note.filepath, note.to_markdown())
|
|
155
160
|
|
|
@@ -590,9 +595,15 @@ def reject_note(note_id: str, reason: Optional[str] = None) -> bool:
|
|
|
590
595
|
|
|
591
596
|
def update_note(note_obj: Note, add_to_index: bool = True) -> bool:
|
|
592
597
|
"""
|
|
593
|
-
|
|
598
|
+
更新已有笔记(规范化写,#549)。
|
|
594
599
|
|
|
595
|
-
处理:查找旧文件 → 更新 updated 时间戳 →
|
|
600
|
+
处理:查找旧文件 → 更新 updated 时间戳 → 写入规则派生路径(expected_filepath)
|
|
601
|
+
→ 删除旧文件(如路径变化)→ 重钉 pin → 更新索引。
|
|
602
|
+
|
|
603
|
+
规范化写语义:写盘目标始终按当前字段(type/title/topic)现算,不用加载时
|
|
604
|
+
钉住的 filepath——否则钉路径后改名判据恒假,改标题/改 type 不再改名移动
|
|
605
|
+
(E2b 回归,tests/unit/test_edit.py:72-82、:373-390)。发散名笔记被触碰时
|
|
606
|
+
由此自然自愈为规则名(单文件)。
|
|
596
607
|
|
|
597
608
|
Args:
|
|
598
609
|
note_obj: 已修改的 Note 对象(必须已有 id)
|
|
@@ -611,13 +622,18 @@ def update_note(note_obj: Note, add_to_index: bool = True) -> bool:
|
|
|
611
622
|
# 更新时间戳
|
|
612
623
|
note_obj.updated = datetime.now()
|
|
613
624
|
|
|
614
|
-
#
|
|
615
|
-
|
|
625
|
+
# 规范化写(#549):目标路径按当前字段现算,不看加载 pin。
|
|
626
|
+
target = note_obj.expected_filepath
|
|
627
|
+
_atomic_write(target, note_obj.to_markdown())
|
|
616
628
|
|
|
617
629
|
# 如果文件路径变了(标题修改导致重命名),删除旧文件
|
|
618
|
-
if old_filepath !=
|
|
630
|
+
if old_filepath != target and old_filepath.exists():
|
|
619
631
|
old_filepath.unlink()
|
|
620
|
-
logger.info(f"Renamed note file: {old_filepath} -> {
|
|
632
|
+
logger.info(f"Renamed note file: {old_filepath} -> {target}")
|
|
633
|
+
|
|
634
|
+
# #549:写盘成功后把 pin 重钉到刚写入的路径——否则同一对象随后的
|
|
635
|
+
# save_note(就地写)会按陈旧 pin 写回旧路径、复活同 id 双文件。
|
|
636
|
+
note_obj.set_filepath(target)
|
|
621
637
|
|
|
622
638
|
logger.info(f"Updated note {note_obj.id}")
|
|
623
639
|
|
|
@@ -417,8 +417,9 @@ def _sync_keep_backlinks(
|
|
|
417
417
|
"""把迁移来的来源写入保留笔记的 backlinks,并移除旧笔记残留。
|
|
418
418
|
|
|
419
419
|
采用 re-read-and-merge:只改 backlinks 字段,避免覆盖并发修改。
|
|
420
|
-
文件定位用 find_note_file(按 cfg
|
|
421
|
-
|
|
420
|
+
文件定位用 find_note_file(按 cfg 搜索);不依赖 Note.filepath property
|
|
421
|
+
—— #549 起 from_markdown 已钉住加载路径(Note.filepath 对加载笔记可信),
|
|
422
|
+
本函数直接操作路径、不加载 Note 对象,保留 find_note_file 定位仍最稳妥。
|
|
422
423
|
"""
|
|
423
424
|
from .config import config
|
|
424
425
|
from .note import find_note_file
|
|
@@ -193,9 +193,10 @@ def test_add_member_non_permanent_warns_but_applies(seeded_kb):
|
|
|
193
193
|
def test_add_member_loads_note_with_legacy_filename(seeded_kb):
|
|
194
194
|
"""#407/#408 legacy 文件名(与标题派生名不一致):不得误判 ghost(#505 CR issue-1)。
|
|
195
195
|
|
|
196
|
-
from_markdown
|
|
196
|
+
#549 前 from_markdown 不钉路径,filepath 属性按标题现算
|
|
197
197
|
`20260820000003-zima-three.md`;真实文件用 legacy 名落盘时现算路径不存在,
|
|
198
|
-
|
|
198
|
+
当时 add-member 会误报 "Member note not found"。#549 后由 from_markdown 钉路径
|
|
199
|
+
根治,本用例保留作回归护栏。
|
|
199
200
|
"""
|
|
200
201
|
member_three = Note(
|
|
201
202
|
id="20260820000003",
|