diff --git a/.codex-plugin/plugin.json b/.codex-plugin/plugin.json index 3b75dcdb..8e5c5fab 100644 --- a/.codex-plugin/plugin.json +++ b/.codex-plugin/plugin.json @@ -4,10 +4,10 @@ "description": "Jyotish local Codex plugin wrapper for Vedic astrology skills and MCP tools.", "author": { "name": "jyotish-skill", - "url": "https://github.com/732642856/yinduzhanxing" + "url": "https://git.copse.top/root/Jyotisha" }, - "homepage": "https://github.com/732642856/yinduzhanxing", - "repository": "https://github.com/732642856/yinduzhanxing", + "homepage": "https://git.copse.top/root/Jyotisha", + "repository": "https://git.copse.top/root/Jyotisha", "license": "MIT", "keywords": [ "jyotish", @@ -36,7 +36,7 @@ "Read", "Write" ], - "websiteURL": "https://github.com/732642856/yinduzhanxing", + "websiteURL": "https://git.copse.top/root/Jyotisha", "defaultPrompt": [ "用户如果只提供出生信息、没有具体问题,请不要要求用户自己想问题。", "环境允许时,先运行 python3 scripts/user_invocation_acceptance_check.py 确认普通用户调用质量门。", diff --git a/AGENTS.md b/AGENTS.md index efb63b36..e4d33aaa 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -73,7 +73,7 @@ | 真人验收清单 | `docs/testing/` | 自动化做不了的浏览器级验收写成可照做的条目 | | 领域术语 | `CONTEXT.md` | 新术语先加到 glossary 再用 | -`progress.md`、`findings.md`、`task_plan.md` 是早期实现日志,不是运行或部署说明,不再追加。 +`docs/history/progress.md`、`docs/history/findings.md`、`docs/history/task_plan.md` 是早期实现日志,不是运行或部署说明,不再追加。 ## 5. Bug History Workflow Hard Constraint diff --git a/BLOCKED.md b/BLOCKED.md index 9633cf2b..4a7ecd78 100644 --- a/BLOCKED.md +++ b/BLOCKED.md @@ -1,5 +1,23 @@ # BLOCKED +## 仓库整备任务 1:发布门仍有三条非环境红(2026-09-03,分支 `codex/repo-hygiene-20260903`) + +任务 0 之后,附录里 staging 独有的 6 条里,夹具大运 / 用户调用验收 / 技能包验收已绿。剩下 3 条**不改期望值、不改校正打分**: + +| 测试 | 现象 | 分类 | 处置 | +| --- | --- | --- | --- | +| `test_local_accuracy_report_outputs_machine_readable_baseline` | `valid_packets` 3 ≥ 4;`dasha_shadbala_oracle_cases.json` 4 包里 1 个仍是 `template_only` | 参考值/oracle 未齐 | 不改断言、不升级模板包。公开案例复验已 `66/66`。 | +| `test_historical_event_priority_preserves_vimshottari_actor_difference` | `selection_priority == 0.0`(1879-03-14 相邻分钟、1922 career) | 校正引擎 / Vimshottari 边界,本轮禁止改阈值 | 交产品 | +| `test_v3_development_result_stays_shadow_only` | `v3_improved_case_count` 1 ≠ 0 | 同上 | 交产品 | + +因此 `run_quality_gate.py --profile release` **不能**在本轮声称退出 0。全量 pytest 里附录那 83 条生产同样红的环境/过期项不在本轮修。 + +复现: + +```bash +.venv/bin/python -m pytest -q tests/test_local_accuracy_report.py tests/test_dynamic_rectification_fact_priority.py::test_historical_event_priority_preserves_vimshottari_actor_difference tests/test_minute_rectification_development.py::test_v3_development_result_stays_shadow_only +``` + ## 生时校正会话面:消除空白假死与交互摩擦(2026-09-03,分支 `codex/rectification-ux-20260903`,重做) - **浏览器级手工验收未做:执行环境无登录态、无 Chrome。** 任务书任务 0 的手工项与任务 1 的整轮录屏(开场 → 3 道选择题 → 候选 → 采用)都做不了;已按任务书把它们写进 `docs/testing/staging-manual-walkthrough-20260901.md` 第 8 节(含深链/刷新与缺口重试两条),交给有真实会话的人。本轮的替代证据:`tests/rectification-surface-state.test.ts`(纯函数会话态/缺口态、hydration 超时/失败、turn 解析)与 `tests/rectification-surface-contract.test.ts`(源码锁:一次揭幕、prepare 阶段 hydration、无重挂、续接同一行、空态、板首态)。 diff --git a/README.md b/README.md index 101bca45..d8a1e119 100644 --- a/README.md +++ b/README.md @@ -156,7 +156,7 @@ The Python engine routes the technique registry (`references/technique_registry. | Skill 方法与真相覆盖层 | [`SKILL.md`](SKILL.md)、`skills/*/SKILL.md`、`references/` | | 引擎/基础设施历史错误台账 | [`docs/research/pre_work_error_ledger.md`](docs/research/pre_work_error_ledger.md) | | 架构决策记录 | [`docs/adr/`](docs/adr/) | -| 早期实现日志(不是运行说明) | `progress.md`、`findings.md`、`task_plan.md`、`CHANGELOG_v6.2_to_v6.9.md` | +| 早期实现日志(不是运行说明) | [`docs/history/`](docs/history/README.md) | ## 隐私与安全红线 diff --git a/SKILL.md b/SKILL.md index ca53a703..324f91f0 100644 --- a/SKILL.md +++ b/SKILL.md @@ -1,7 +1,7 @@ --- name: jyotish-vedic-astrology version: 6.9.14 -description: "印度占星(Jyotish)商业解盘与推运系统。核心能力:PDF星盘输入→严谨解盘→受合同约束的推运方向输出。Dasha、Yoga、分盘、合盘与补救建议按计算回执和能力状态交付;精确应期及受限技法不会作为确定性商业结论。触发词:印度占星、吠陀占星、Jyotish、解盘、推运、星盘分析、Dasha、Transit、Nakshatra、Yoga。GitHub: https://github.com/732642856/yinduzhanxing" +description: "印度占星(Jyotish)商业解盘与推运系统。核心能力:PDF星盘输入→严谨解盘→受合同约束的推运方向输出。Dasha、Yoga、分盘、合盘与补救建议按计算回执和能力状态交付;精确应期及受限技法不会作为确定性商业结论。触发词:印度占星、吠陀占星、Jyotish、解盘、推运、星盘分析、Dasha、Transit、Nakshatra、Yoga。Repo: https://git.copse.top/root/Jyotisha" --- # 印度占星专业解盘与推运系统 diff --git a/CHANGELOG_v6.2_to_v6.9.md b/docs/history/CHANGELOG_v6.2_to_v6.9.md similarity index 100% rename from CHANGELOG_v6.2_to_v6.9.md rename to docs/history/CHANGELOG_v6.2_to_v6.9.md diff --git a/docs/history/README.md b/docs/history/README.md new file mode 100644 index 00000000..b5b00de7 --- /dev/null +++ b/docs/history/README.md @@ -0,0 +1,8 @@ +# 早期实现日志 + +这里是 2026-06~08 的实现日志,不是运行说明,也不再追加。 + +- `progress.md` +- `findings.md` +- `task_plan.md` +- `CHANGELOG_v6.2_to_v6.9.md` diff --git a/findings.md b/docs/history/findings.md similarity index 100% rename from findings.md rename to docs/history/findings.md diff --git a/progress.md b/docs/history/progress.md similarity index 100% rename from progress.md rename to docs/history/progress.md diff --git a/task_plan.md b/docs/history/task_plan.md similarity index 100% rename from task_plan.md rename to docs/history/task_plan.md diff --git a/docs/tasks/PROGRESS-repo-hygiene-20260903.md b/docs/tasks/PROGRESS-repo-hygiene-20260903.md index 3d433bc8..144b55b9 100644 --- a/docs/tasks/PROGRESS-repo-hygiene-20260903.md +++ b/docs/tasks/PROGRESS-repo-hygiene-20260903.md @@ -3,70 +3,89 @@ 工作树:`/Users/jesse/Downloads/Copse/astrology/.worktrees/repo-hygiene-20260903` 分支:`codex/repo-hygiene-20260903` 基线:开工后快进到 `origin/staging` @ `58cae371`(含 BUG-510 与上游同步任务书)。任务书:`docs/tasks/TASK-repo-hygiene-20260903.md`。 -未碰 `main`。未改 `DEFAULT_AYANAMSA_NAME`(保持 `raman`)。未改校正打分/阈值/golden。未改 `page.tsx`(仍 2041 行)。`scripts/jyotish_api_server.py` 仍 11120 行(与 rebase 后 HEAD 相同)。 +未碰 `main`。未改 `DEFAULT_AYANAMSA_NAME`(保持 `raman`)。未改校正打分/阈值/golden。未改 `page.tsx`(仍 2041 行)。`scripts/jyotish_api_server.py` 仍 11120 行。 -BUG 编号:开工时 `docs/BUG_HISTORY.md` 最大号是 **BUG-510**。本轮岁差记 **BUG-511**。 +BUG 编号:开工时最大号 **BUG-510**。岁差记 **BUG-511**。 | 任务 | 状态 | 说明 | | --- | --- | --- | -| 0 岁差设置 + 参考集锁口径 | 待验收 | 本提交。产品 staging 实测仍欠 | -| 1 发布门本地可复现 | 未开始 | 任务 0 先推 | -| 2 注册表白名单 | 未开始 | | -| 3 仓库地址 | 未开始 | | -| 4 早期日志搬家 | 未开始 | | -| 5 远端分支清理 | 未开始 | | +| 0 岁差设置 + 参考集锁口径 | 待验收 | `b6a70aa7` 已推功能分支。产品 staging 实测仍欠 | +| 1 发布门本地可复现 | 部分完成 | 选项 (b) 已做;release profile 因 3 条 BLOCKED 不能声称退出 0 | +| 2 注册表白名单 | 完成 | `audit_capabilities.py --mode validate` → `valid=true, problem_count=0` | +| 3 仓库地址 | 完成 | Gitea 产品仓;slug 解析用例保留 | +| 4 早期日志搬家 | 完成 | 根目录 `ls *.md` 只剩规定 8 个(外加 gitignore 的 `COVERAGE_AUDIT_REPORT.md`) | +| 5 远端分支清理 | 待执行删除 | 已 `fetch --prune` 核对名单,见下 | ## 环境备忘 - 本 worktree **没有** `.venv`。预检与 Python 测试用主仓 `/Users/jesse/Downloads/Copse/astrology/yinduzhanxing/.venv`(`mcp` 1.28.1,`hypothesis` 已装)。 -- 任务书写把迁移放进 `frontend/db/migrations/`。实际 `frontend/scripts/db-migrate.mjs` 会跑 `frontend/db/migrations` **和** `frontend/supabase/migrations`。账户资料列修复历来放 supabase(`account-api` 还断言同类 SQL 不在 `db/migrations`)。本轮唯一迁移是 `frontend/supabase/migrations/20260903010000_profile_ayanamsa.sql`,避免两个目录同名撞车。 -- 产品文案里的「账户与出生资料」在现界面是 **星盘资料** 里的本人编辑表,不是 `个人资料`(MVP 合同禁止在那里放出生 UI)。岁差单选加在本人星盘表单,未改 `page.tsx`。 -- `npm run test:db` 与 `npx tsx --test tests/rectification-*.test.ts` 不可并行:默认 `JYOTISHA_POSTGRES_MAX_FIXTURES=2`,抢槽时前几个库测会报泛化 `database migration failed`。隔离重跑后绿。 +- 本地错装 `mcp 2.x` / 缺 `hypothesis` 是环境问题,不改 pin。`requirements.txt` 仍是 `mcp>=1.0,<2`;`requirements-dev.txt` 已有 `hypothesis>=6.0`。 +- 任务书写把迁移放进 `frontend/db/migrations/`。实际 migrator 跑 supabase + db 两目录。本轮唯一迁移:`frontend/supabase/migrations/20260903010000_profile_ayanamsa.sql`。 +- 「账户与出生资料」在现界面是 **星盘资料** 本人编辑表。岁差单选在那里,未改 `page.tsx`,未进 `个人资料`。 +- `npm run test:db` 不可与 `rectification-*.test.ts` 并行(fixture 槽默认 2)。 +- `python3 scripts/pre_work_check.py` 在本 worktree 用 Anaconda 3.12:`remote_visibility_status=verified`(不再因旧 slug blocked)。失败项是 `fragment_scan` 90s timeout 与 focused tests 45s timeout,属环境预算,不是 slug。 ## 任务 0 -### 0.a 参考集锁口径 +见提交 `b6a70aa7`。公开案例复验 `valid: true`、gated **66/66**、`pass_rate: 1.0`。`npm run test:db` **34/34**。产品虚构盘实测仍欠。部署 SHA 待合入 staging 后补。 -- `tests/run_real_case_revalidation.py`:`REFERENCE_AYANAMSA = "lahiri"`,引擎 argv `--ayanamsa lahiri`,报告 `reference_ayanamsa`。 -- `scripts/local_accuracy_report.py` 同口径。 -- 夹具大运:`base` 加 `--ayanamsa lahiri`。三栏:原值=依赖默认;新值=显式 lahiri;原因=夹具日期是 Lahiri 口径。期望日期未改。 +### 0.a 三栏 -### 0.b 标签与计算一致 - -- `scripts/jyotish_api_server.py` 经 `_request_ayanamsa` / `DEFAULT_AYANAMSA_NAME` / `ayanamsa_display_name`,不再写死 `'lahiri'` / `'Raman'`。 -- `_request_ayanamsa` 也读 `ayanamsa_policy`,VedAstro 对照与该次请求同口径。 -- `DEFAULT_AYANAMSA_NAME` 仍是 `'raman'`。 -- 验收 grep(`scripts/jyotish_api_server.py` + `frontend/src`,排除 test):只剩 `frontend/src/lib/ayanamsa.ts` 选项/默认,以及 `personal-report-generation.ts` 的 `SAFE_AYANAMSA` 显示映射。 - -### 0.c 用户设置 - -- `frontend/src/lib/ayanamsa.ts`:`resolveAyanamsa`,默认 `raman`。 -- 迁移:`profiles.ayanamsa text not null default 'raman'` + 四值 check + `grant update (ayanamsa)` 给 `authenticated`。 -- 咨询、声明窗口、报告 worker、每日星语、合盘、校正 v9 引擎请求、生时评估 scan 均传实际岁差。 -- 校正 `V9BaselineSnapshot` 新写入带 `ayanamsa`;**不进** `baselineFingerprint`。 -- 岁差不进 `declarationFields` / `concurrencyFields`,改设置不 409、不清确认。 +夹具大运 `base`:原值=依赖默认岁差;新值=显式 `--ayanamsa lahiri`;原因=夹具日期是 Lahiri 口径。期望日期未改。 ### 偏离 -- 迁移目录:supabase,原因见环境备忘。 -- UI 位置:星盘资料本人编辑,不是不存在的「账户与出生资料」面板。 +- 迁移目录:supabase。 +- UI:星盘资料本人编辑,不是不存在的「账户与出生资料」面板。 -### 命令摘要 +## 任务 1 -| 命令 | 结果 | -| --- | --- | -| `python tests/run_real_case_revalidation.py` | `valid: true`,`reference_ayanamsa: lahiri`,gated **66/66**,`pass_rate: 1.0` | -| `pytest …test_fixture_dasha_timeline…` + `test_api_server_growth_contract.py` | 4 passed;夹具日期未改;API 行数 11120 | -| `scripts/user_invocation_acceptance_check.py` | `status: pass`,`checks.user_invocation_tests: true`,`external_adapter_status: complete`(本机 PyJHora 可用) | -| `npx tsc --noEmit` | 0 错 | -| `npx eslint .` | 0 error,73 既有 warning | -| 前端聚焦:ayanamsa / account-api / consultation-route-service / server-owned-birth-profile / global-birth-profile-routes / settings-mvp / rectification-v9-case-service / consultation-workflow-request / consultation-birth-accuracy / consultation-agentic-runtime / personal-report-api / rectification-v9-engine-contract | fail=0 | -| `npm run test:db`(`--test-concurrency=1`,单独跑) | **34 pass / 0 fail** | -| `npx tsx --test tests/rectification-*.test.ts` 隔离重跑库测 | ingest P0 + v9-database **10 pass / 0 fail**;未改 scorer/golden | -| `wc -l frontend/src/app/page.tsx` | 2041,与 HEAD 相同 | +选 **(b)**:PyJHora `missing_dependency` 时 `user_invocation_acceptance_check` 的 `status` 为 `partial`,退出码 0。硬错误仍 `fail`。 -产品负责人 staging 实测(虚构盘 2000-01-01 12:00 UTC 0/0:Lahiri=Swati,Raman=Vishakha)仍欠。部署 SHA 待合入 staging 后补。 +三栏(`test_one_command_user_invocation_acceptance_check` / 技能包验收):原值=`status == "pass"`;新值=`pass` 或 `partial`;原因=外部参照引擎缺席应降级不应失败。`external_adapter_status` 仍必须在 `{pass, partial, complete}`。 -## 任务 1–5 +测试不改写仓库:`character_level_inventory_manifest.py` 增加 `--output-dir`;写报告的测试改写 `tmp_path`。`numeric_oracle_gap_queue_v3.py` / `hard_gap_source_hunt_2026_07_23.py` 的测试加 `--no-write`。跑完后 `git status` 无 `docs/research/character_level_*_latest.*`、无那两份 oracle JSON 被改写。 -尚未开始。 +### 全量红名单处理表 + +附录 6 条 staging 独有: + +| 测试 | 分类 | 本轮 | +| --- | --- | --- | +| `test_fixture_dasha_timeline_rejects_workbuddy_regression_claims` | 任务 0.a 口径 | 绿 | +| `test_one_command_user_invocation_acceptance_check` | 连带 + 选项 b | 绿(本机 PyJHora 可用时 `pass`) | +| `test_premium_skill_zip_runs_from_clean_directory` | 连带 + 选项 b | 绿 | +| `test_local_accuracy_report.py`(`valid_packets >= 4`) | 参考值/oracle:4 包 1 个 `template_only`,校验 3 valid | BLOCKED,不改值 | +| `test_historical_event_priority_preserves_vimshottari_actor_difference` | 校正引擎 | BLOCKED | +| `test_v3_development_result_stays_shadow_only` | 校正引擎 | BLOCKED | + +附录 83 条生产同样红、13 个收集 ERROR(缺 hypothesis / mcp 2.x):环境类。本机 CI 同款 venv 已有 hypothesis 与 mcp 1.x,收集 ERROR 应消失。未在本轮把 `run_quality_gate.py --profile release` 跑到退出 0。 + +## 任务 2 + +- 白名单加 `experimental_variant`、`comparison_only`;`calculation` 允许 `experimental`(注册表现况);`rule`/`prediction` 允许 `blocked`。 +- `comparison_only` 计入 `comparison_only_entries`,政策位与 `audit_only` 同等:不能影响结论。 +- 断言:`experimental_variant` 的 prediction/rule 必须 `blocked`,`user_visibility` 必须 `expert_audit`。 +- `scripts/audit_capabilities.py --mode validate`:`valid=true, problem_count=0`(既有 warning:Rangacharya knowledge_ref 缺一篇 spec)。 + +## 任务 3 + +- `pyproject.toml`、`.codex-plugin/plugin.json`、根 `SKILL.md`、`skill_release_package.py`、`skill_release_manifest.py`、`skills/jyotish-engine-modules/SKILL.md` 改为 `https://git.copse.top/root/Jyotisha`(Bug Reports `/issues`,Documentation 指向 Gitea 上的 `README.md`)。 +- 发行测试三栏:原值=上游研究仓 GitHub;新值=产品 Gitea;原因=安装说明必须指向产品仓。 +- `remote_repo_visibility_check.py`:非 GitHub remote 不再退回 `732642856/yinduzhanxing`;报告 `provider: gitea`,用 `git ls-remote`。slug 解析用例未改,新增 Gitea 用例。 +- 任务书 grep 剩余:`tests/test_remote_repo_visibility_check.py` 的 GitHub slug 解析行(任务书要求保留)。`COVERAGE_AUDIT_REPORT.md` 在 `.gitignore`。hashed `skills/*/versions`、`references/`、`docs/`、import 测试按任务书排除。 + +## 任务 4 + +`git mv` 四份早期日志到 `docs/history/`,新增 `docs/history/README.md`。`RELEASE_CRITICAL_UNTRACKED_PATHS` 三个路径改为 `docs/history/...`。`README.md` 文档地图与 `AGENTS.md` §4 最后一行已同步。`frontend/tests/staging-backend-workflows.test.ts` 的 docs-only 名单跟着改,避免根路径幽灵。 + +## 任务 5 + +`git fetch origin --prune` 后,`git branch -r --merged origin/staging` 相对任务书名单: + +- 名单 67 条全部仍在,不少。 +- 多出已合入、**不删**(不在名单):`codex/rectification-ux-20260903`。 +- 未合入、**不删**:`codex/rectification-ux-20260902`、`codex/diagnose-rectification-request-20260827`、`fix/staging-gate-checkout-retry`,外加本分支 `codex/repo-hygiene-20260903`。 +- `git ls-remote --heads origin` 当前 **74**。删名单后预期约 7(`main`、`staging`、本分支、3 条未合入保留、外加已合入但未列入名单的 `rectification-ux-20260903`)。 + +删除命令执行结果写在本段之后。 diff --git a/frontend/tests/staging-backend-workflows.test.ts b/frontend/tests/staging-backend-workflows.test.ts index efe036da..0f789832 100644 --- a/frontend/tests/staging-backend-workflows.test.ts +++ b/frontend/tests/staging-backend-workflows.test.ts @@ -1368,9 +1368,9 @@ test("gated paths cover every image input, package input, and gate-read reposito "TASK-example-20260901.md", "PROGRESS-example-20260901.md", "CHANGELOG.md", - "progress.md", - "task_plan.md", - "findings.md", + "docs/history/progress.md", + "docs/history/task_plan.md", + "docs/history/findings.md", "BLOCKED.md", "CONTEXT.md", "AGENTS.md", diff --git a/pyproject.toml b/pyproject.toml index 2c41f44d..47f1a5c1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -51,11 +51,11 @@ jyotish-engine = "jyotish_vedic.engine:cli_main" jyotish-mcp = "jyotish_vedic.mcp_server:main" [project.urls] -Homepage = "https://github.com/732642856/yinduzhanxing" -Documentation = "https://github.com/732642856/yinduzhanxing#readme" -Repository = "https://github.com/732642856/yinduzhanxing" -"Bug Reports" = "https://github.com/732642856/yinduzhanxing/issues" -Changelog = "https://github.com/732642856/yinduzhanxing/blob/main/CHANGELOG.md" +Homepage = "https://git.copse.top/root/Jyotisha" +Documentation = "https://git.copse.top/root/Jyotisha/src/branch/main/README.md" +Repository = "https://git.copse.top/root/Jyotisha" +"Bug Reports" = "https://git.copse.top/root/Jyotisha/issues" +Changelog = "https://git.copse.top/root/Jyotisha" [tool.setuptools.packages.find] where = ["."] diff --git a/scripts/capability_evidence_pool.py b/scripts/capability_evidence_pool.py index 06899047..fa83128d 100644 --- a/scripts/capability_evidence_pool.py +++ b/scripts/capability_evidence_pool.py @@ -30,6 +30,7 @@ def build_capability_evidence_pool_summary(registry: dict[str, Any] | None = Non prediction_counts = Counter() primary_entries: list[str] = [] audit_only_entries: list[str] = [] + comparison_only_entries: list[str] = [] alias_entries: list[str] = [] for tech_id, tech in techniques.items(): @@ -50,6 +51,8 @@ def build_capability_evidence_pool_summary(registry: dict[str, Any] | None = Non primary_entries.append(tech_id) elif evidence_role == "audit_only": audit_only_entries.append(tech_id) + elif evidence_role == "comparison_only": + comparison_only_entries.append(tech_id) elif evidence_role == "alias": alias_entries.append(tech_id) @@ -67,12 +70,14 @@ def build_capability_evidence_pool_summary(registry: dict[str, Any] | None = Non "prediction_verification_counts": dict(sorted(prediction_counts.items())), "primary_entries": sorted(primary_entries), "audit_only_entries": sorted(audit_only_entries), + "comparison_only_entries": sorted(comparison_only_entries), "alias_entries": sorted(alias_entries), "conclusion_policy": { "primary_chain_required": True, "all_89_entries_must_not_be_flattened_into_conclusions": True, "supporting_entries_can_only_raise_or_lower_confidence": True, "audit_only_entries_cannot_affect_astrological_conclusions": True, + "comparison_only_entries_cannot_affect_astrological_conclusions": True, "conflicts_must_downgrade_confidence": True, }, } diff --git a/scripts/character_level_inventory_manifest.py b/scripts/character_level_inventory_manifest.py index c898f77d..b41d3e92 100644 --- a/scripts/character_level_inventory_manifest.py +++ b/scripts/character_level_inventory_manifest.py @@ -161,6 +161,19 @@ def _relative(path: Path) -> str: return str(path.relative_to(ROOT)) +def _artifact_ref(path: Path) -> str: + try: + return path.resolve().relative_to(ROOT.resolve()).as_posix() + except ValueError: + return str(path) + + +def _relocate_report(path: Path, output_dir: Path | None) -> Path: + if output_dir is None: + return path + return Path(output_dir) / path.name + + def _should_skip(path: Path) -> bool: try: rel_parts = path.relative_to(ROOT).parts @@ -1313,9 +1326,21 @@ def main(argv: list[str] | None = None) -> int: ) parser.add_argument("--no-write", action="store_true", help="Print the manifest without writing report artifacts.") parser.add_argument("--summary-only", action="store_true", help="Print only summary fields; still writes full artifacts unless --no-write is set.") + parser.add_argument( + "--output-dir", + default="", + help="Write report artifacts here instead of docs/research. Tests must pass a tmp directory.", + ) args = parser.parse_args(argv) - report = build_manifest(scope=args.scope, write=not args.no_write) + report = build_manifest(scope=args.scope, write=False) + output_dir = Path(args.output_dir) if args.output_dir else None + json_path = _relocate_report(ROOT / report["artifacts"]["json_report"], output_dir) + markdown_path = _relocate_report(ROOT / report["artifacts"]["markdown_report"], output_dir) + report["artifacts"]["json_report"] = _artifact_ref(json_path) + report["artifacts"]["markdown_report"] = _artifact_ref(markdown_path) + if not args.no_write: + _write_reports(report, json_report=json_path, markdown_report=markdown_path) printable = _summary_view(report) if args.summary_only else report print(json.dumps(printable, ensure_ascii=False, indent=2)) return 0 if report["status"] == "pass" else 1 diff --git a/scripts/hard_gap_source_hunt_2026_07_23.py b/scripts/hard_gap_source_hunt_2026_07_23.py index a7b81d23..234bb64c 100644 --- a/scripts/hard_gap_source_hunt_2026_07_23.py +++ b/scripts/hard_gap_source_hunt_2026_07_23.py @@ -17,5 +17,9 @@ ROWS=[ def build(): return {'scope':'hard_gap_source_hunt','created_at':'2026-07-23','claim_status':'source_hunt_only','truth_matrix_allowed':False,'production_tuning_allowed':False,'rows':ROWS,'summary':{'source_count':len(ROWS),'ready_numeric_oracle_count':0,'partial_numeric_candidate_count':1,'timing_holdout_status':'blocked_until_independent_human_labels'},'next_actions':['If RoxyAPI key/terms are available, capture pinned KP /cusps raw for a public fixed chart and compare against VedicAstro observation only.','For Gulika fragment, locate place/timezone or keep candidate blocked.','For Tajika/Saham, require solar return input plus expected Saham longitude before replay.','For timing/rectification, freeze independent human labels before blind ranking.'],'boundary':'No source found in this pass satisfies complete input + expected numeric values + replay raw/hash. All hard gaps remain blocked or candidate-only.'} def main(): - data=build(); OUT.write_text(json.dumps(data,ensure_ascii=False,indent=2)+'\n'); print(json.dumps(data,ensure_ascii=False,indent=2)); return 0 + import argparse + ap=argparse.ArgumentParser(); ap.add_argument('--no-write', action='store_true'); args=ap.parse_args(); data=build() + if not args.no_write: + OUT.write_text(json.dumps(data,ensure_ascii=False,indent=2)+'\n') + print(json.dumps(data,ensure_ascii=False,indent=2)); return 0 if __name__=='__main__': raise SystemExit(main()) diff --git a/scripts/numeric_oracle_gap_queue_v3.py b/scripts/numeric_oracle_gap_queue_v3.py index d3e74aa6..d676d0b8 100644 --- a/scripts/numeric_oracle_gap_queue_v3.py +++ b/scripts/numeric_oracle_gap_queue_v3.py @@ -17,5 +17,9 @@ ROWS=[ def build(): return {'scope':'numeric_oracle_gap_queue_v3','created_at':'2026-07-23','claim_status':'candidate_queue','truth_matrix_allowed':False,'production_tuning_allowed':False,'rows':ROWS,'summary':{'candidate_count':len(ROWS),'numeric_packet_ready_count':0,'partial_numeric_candidate_count':sum(r['status']=='partial_numeric_candidate' for r in ROWS),'blocked_domains':sorted(set(r['domain'] for r in ROWS))},'promotion_requirements':['complete input','expected numeric values','source URL/page/line or screenshot hash','license/copyright-safe boundary','local replay raw/hash','claim gate remains non-truth until cross-source consistency'],'boundary':'This queue records public candidates only. It does not upgrade KP, Prashna, Tajika, Saham, Gulika or Sphuta truth status.'} def main(): - data=build(); OUT.write_text(json.dumps(data,ensure_ascii=False,indent=2)+'\n'); print(json.dumps(data,ensure_ascii=False,indent=2)); return 0 + import argparse + ap=argparse.ArgumentParser(); ap.add_argument('--no-write', action='store_true'); args=ap.parse_args(); data=build() + if not args.no_write: + OUT.write_text(json.dumps(data,ensure_ascii=False,indent=2)+'\n') + print(json.dumps(data,ensure_ascii=False,indent=2)); return 0 if __name__=='__main__': raise SystemExit(main()) diff --git a/scripts/remote_repo_visibility_check.py b/scripts/remote_repo_visibility_check.py index c970e87e..bb8c74b3 100644 --- a/scripts/remote_repo_visibility_check.py +++ b/scripts/remote_repo_visibility_check.py @@ -19,7 +19,14 @@ from urllib import request ROOT = Path(__file__).resolve().parents[1] -DEFAULT_GITHUB_SLUG = "732642856/yinduzhanxing" + + +def classify_remote_provider(url: str) -> str: + if github_slug_from_remote_url(url): + return "github" + if re.search(r"git\.copse\.top[:/]", url.strip()): + return "gitea" + return "other" def run_command(args: list[str], timeout: int) -> dict[str, Any]: @@ -103,21 +110,39 @@ def build_report(timeout: int) -> dict[str, Any]: local_branch = run_command(["git", "rev-parse", "--abbrev-ref", "HEAD"], timeout) local_head = run_command(["git", "rev-parse", "HEAD"], timeout) urls = git_remote_urls(timeout) - slug = next((github_slug_from_remote_url(url) for url in urls if github_slug_from_remote_url(url)), DEFAULT_GITHUB_SLUG) - git_checks = [git_ls_remote(url, timeout) for url in urls] or [git_ls_remote(f"https://github.com/{slug}.git", timeout)] - api_check = github_api_branches(slug, timeout) + github_slug = next((github_slug_from_remote_url(url) for url in urls if github_slug_from_remote_url(url)), None) + providers = [classify_remote_provider(url) for url in urls] + if "github" in providers: + provider = "github" + elif "gitea" in providers: + provider = "gitea" + else: + provider = providers[0] if providers else "unknown" + git_checks = [git_ls_remote(url, timeout) for url in urls] git_verified = any(check["ok"] and check["ref_count"] > 0 for check in git_checks) - api_visible = api_check["ok"] - status = "verified" if git_verified else ("web_visible_git_blocked" if api_visible else "blocked") + if github_slug: + api_check = github_api_branches(github_slug, timeout) + api_visible = api_check["ok"] + status = "verified" if git_verified else ("web_visible_git_blocked" if api_visible else "blocked") + else: + api_check = { + "method": "github_api_branches", + "url": None, + "ok": False, + "branches": [], + "error": "not_github_remote", + } + status = "verified" if git_verified else "blocked" return { "scope": "remote_repo_visibility_check", "status": status, + "provider": provider, "must_not_claim_synced": status != "verified", "local": { "branch": local_branch.get("stdout", "").strip(), "head": local_head.get("stdout", "").strip(), }, - "github_slug": slug, + "github_slug": github_slug, "git_checks": git_checks, "github_api": api_check, } diff --git a/scripts/run_quality_gate.py b/scripts/run_quality_gate.py index bbf23222..1c2fd138 100644 --- a/scripts/run_quality_gate.py +++ b/scripts/run_quality_gate.py @@ -122,8 +122,8 @@ RELEASE_CRITICAL_UNTRACKED_PATHS = [ "docs/research/open_source_scan_2026_06_22.md", "docs/research/product_gap_matrix_2026_06_22.md", "docs/research/whole_machine_git_audit_2026_06_23.md", - "findings.md", - "progress.md", + "docs/history/findings.md", + "docs/history/progress.md", "references/oracle/dasha_shadbala_oracle_cases.json", "scripts/audit_fragments.py", "scripts/deep_varga_avastha.py", @@ -134,7 +134,7 @@ RELEASE_CRITICAL_UNTRACKED_PATHS = [ "scripts/oracle_boundary_audit.py", "scripts/oracle_collection_queue.py", "scripts/oracle_evidence_validator.py", - "task_plan.md", + "docs/history/task_plan.md", "tests/test_api_server_security.py", "tests/test_dasha_reference_audit.py", "tests/test_deep_varga_avastha.py", diff --git a/scripts/skill_release_manifest.py b/scripts/skill_release_manifest.py index 54cee616..f2724fe6 100644 --- a/scripts/skill_release_manifest.py +++ b/scripts/skill_release_manifest.py @@ -12,7 +12,7 @@ except ModuleNotFoundError: # pragma: no cover - direct script execution from diagnose_external_engine_adapters import build_report as external_engine_report -REPO_URL = "https://github.com/732642856/yinduzhanxing" +REPO_URL = "https://git.copse.top/root/Jyotisha" def _external_boundary() -> dict[str, Any]: diff --git a/scripts/skill_release_package.py b/scripts/skill_release_package.py index a970072f..5ba99102 100644 --- a/scripts/skill_release_package.py +++ b/scripts/skill_release_package.py @@ -24,7 +24,7 @@ SKIP_NAMES = {".env", ".env.local"} GENERATED_DOCS = { "INSTALL.md": """# Jyotish Skill Install -Basic Git version: https://github.com/732642856/yinduzhanxing +Basic Git version: https://git.copse.top/root/Jyotisha 1. Unzip this package into a local folder. 2. Run `python3 scripts/public_release_privacy_scan.py`. diff --git a/scripts/user_invocation_acceptance_check.py b/scripts/user_invocation_acceptance_check.py index 33ba7182..9bffe86c 100644 --- a/scripts/user_invocation_acceptance_check.py +++ b/scripts/user_invocation_acceptance_check.py @@ -79,6 +79,20 @@ def _strict_summary(report: dict[str, Any]) -> dict[str, Any]: } +def resolve_acceptance_status(errors: list[str], adapter_report: dict[str, Any]) -> str: + """Hard failures stay fail; a missing reference engine is a partial, not a fail.""" + if errors: + return "fail" + engines = adapter_report.get("engines") if isinstance(adapter_report.get("engines"), dict) else {} + for details in engines.values(): + if not isinstance(details, dict): + continue + blockers = details.get("readiness_blockers") or [] + if details.get("missing_dependency") or "missing_dependency" in blockers: + return "partial" + return "pass" + + def main() -> int: pytest_check = _run( [ @@ -162,9 +176,10 @@ def main() -> int: if adapter_report.get("status") not in usable_adapter_statuses: errors.append("external_adapter_status_unusable") + status = resolve_acceptance_status(errors, adapter_report) result = { "scope": "user_invocation_acceptance_check", - "status": "pass" if not errors else "fail", + "status": status, "errors": errors, "checks": { "user_invocation_tests": pytest_check["returncode"] == 0, @@ -177,7 +192,7 @@ def main() -> int: "external_engines": _engine_summary(adapter_report), } print(json.dumps(result, ensure_ascii=False, indent=2, sort_keys=True)) - return 0 if not errors else 1 + return 0 if status != "fail" else 1 if __name__ == "__main__": diff --git a/skills/jyotish-engine-modules/SKILL.md b/skills/jyotish-engine-modules/SKILL.md index 89ef8657..d69d4660 100644 --- a/skills/jyotish-engine-modules/SKILL.md +++ b/skills/jyotish-engine-modules/SKILL.md @@ -64,7 +64,7 @@ cd ~/Projects/yinduzhanxing-local ```bash # 克隆GitHub仓库 cd ~/Projects -git clone https://github.com/732642856/yinduzhanxing.git +git clone https://git.copse.top/root/Jyotisha.git cd yinduzhanxing # 复制新模块 diff --git a/tests/test_capability_evidence_pool.py b/tests/test_capability_evidence_pool.py index cb734b2d..e4abed93 100644 --- a/tests/test_capability_evidence_pool.py +++ b/tests/test_capability_evidence_pool.py @@ -25,24 +25,32 @@ def test_registry_is_backend_evidence_pool_not_flat_user_skill_list() -> None: "composite_adjudicator", "workflow_or_engineering", "alias_entry", + "experimental_variant", } - allowed_roles = {"primary", "secondary", "context", "audit_only", "alias"} + allowed_roles = {"primary", "secondary", "context", "audit_only", "alias", "comparison_only"} allowed_visibility = {"ordinary_topic_router", "expert_audit", "hidden"} allowed_prediction = { "case_validated_partial", "support_only", "not_claimed", "not_applicable", + "blocked", } + allowed_rule = {"verified", "partial", "not_applicable", "blocked"} + allowed_calculation = {"verified", "partial", "not_applicable", "experimental"} for tech_id, tech in techniques.items(): assert tech["entry_type"] in allowed_entry_types, tech_id assert tech["evidence_role"] in allowed_roles, tech_id assert tech["user_visibility"] in allowed_visibility, tech_id - assert tech["verification_level"]["calculation"] in {"verified", "partial", "not_applicable"}, tech_id - assert tech["verification_level"]["rule"] in {"verified", "partial", "not_applicable"}, tech_id + assert tech["verification_level"]["calculation"] in allowed_calculation, tech_id + assert tech["verification_level"]["rule"] in allowed_rule, tech_id assert tech["verification_level"]["prediction"] in allowed_prediction, tech_id assert tech["conclusion_policy"], tech_id + if tech["entry_type"] == "experimental_variant": + assert tech["verification_level"]["prediction"] == "blocked", tech_id + assert tech["verification_level"]["rule"] == "blocked", tech_id + assert tech["user_visibility"] == "expert_audit", tech_id assert techniques["case_validator"]["evidence_role"] == "audit_only" assert techniques["thematic_report_orchestrator"]["entry_type"] == "workflow_or_engineering" @@ -59,6 +67,8 @@ def test_evidence_pool_summary_routes_few_primary_items_and_many_support_items() assert summary["evidence_role_counts"]["primary"] >= 8 assert summary["evidence_role_counts"]["secondary"] > summary["evidence_role_counts"]["primary"] assert summary["evidence_role_counts"]["audit_only"] >= 3 + assert summary["comparison_only_entries"] == ["rangacharya_jaimini_variant"] + assert summary["conclusion_policy"]["comparison_only_entries_cannot_affect_astrological_conclusions"] is True assert summary["prediction_verification_counts"]["not_claimed"] > 0 assert summary["conclusion_policy"]["primary_chain_required"] is True assert summary["conclusion_policy"]["all_89_entries_must_not_be_flattened_into_conclusions"] is True diff --git a/tests/test_character_level_inventory_manifest.py b/tests/test_character_level_inventory_manifest.py index dfee5277..3b25ddc5 100644 --- a/tests/test_character_level_inventory_manifest.py +++ b/tests/test_character_level_inventory_manifest.py @@ -25,6 +25,11 @@ def _run_manifest(*args: str) -> dict: return json.loads(completed.stdout) +def _saved(report: dict, key: str) -> Path: + raw = Path(report["artifacts"][key]) + return raw if raw.is_absolute() else ROOT / raw + + def test_manifest_indexes_project_source_layers_without_heavy_ocr() -> None: report = _run_manifest("--scope", "project", "--no-write") @@ -64,13 +69,14 @@ def test_manifest_indexes_project_source_layers_without_heavy_ocr() -> None: } -def test_manifest_writes_json_and_markdown_reports() -> None: - report = _run_manifest("--scope", "project") +def test_manifest_writes_json_and_markdown_reports(tmp_path: Path) -> None: + report = _run_manifest("--scope", "project", "--output-dir", str(tmp_path)) - json_path = ROOT / report["artifacts"]["json_report"] - md_path = ROOT / report["artifacts"]["markdown_report"] + json_path = _saved(report, "json_report") + md_path = _saved(report, "markdown_report") assert json_path.exists() assert md_path.exists() + assert json_path.parent == tmp_path saved = json.loads(json_path.read_text(encoding="utf-8")) assert saved["summary"] == report["summary"] @@ -136,22 +142,23 @@ def test_external_manifest_indexes_high_relevance_machine_fragments_without_copy assert "text_preview" not in item -def test_external_manifest_writes_separate_reports() -> None: - report = _run_manifest("--scope", "external", "--summary-only") +def test_external_manifest_writes_separate_reports(tmp_path: Path) -> None: + report = _run_manifest("--scope", "external", "--summary-only", "--output-dir", str(tmp_path)) assert "by_path" not in report - assert report["artifacts"]["json_report"] == "docs/research/character_level_external_manifest_latest.json" - assert report["artifacts"]["markdown_report"] == "docs/research/character_level_external_manifest_latest.md" + assert Path(report["artifacts"]["json_report"]).name == "character_level_external_manifest_latest.json" + assert Path(report["artifacts"]["markdown_report"]).name == "character_level_external_manifest_latest.md" - json_path = ROOT / report["artifacts"]["json_report"] - md_path = ROOT / report["artifacts"]["markdown_report"] + json_path = _saved(report, "json_report") + md_path = _saved(report, "markdown_report") assert json_path.exists() assert md_path.exists() + assert json_path.parent == tmp_path assert "External High-Relevance Inventory Manifest" in md_path.read_text(encoding="utf-8") -def test_extraction_queue_report_combines_project_and_external_binary_work() -> None: - report = _run_manifest("--scope", "extraction-queue") +def test_extraction_queue_report_combines_project_and_external_binary_work(tmp_path: Path) -> None: + report = _run_manifest("--scope", "extraction-queue", "--output-dir", str(tmp_path)) assert report["scope"] == "extraction-queue" assert report["status"] == "pass" @@ -171,15 +178,16 @@ def test_extraction_queue_report_combines_project_and_external_binary_work() -> } assert "text_preview" not in item - json_path = ROOT / report["artifacts"]["json_report"] - md_path = ROOT / report["artifacts"]["markdown_report"] + json_path = _saved(report, "json_report") + md_path = _saved(report, "markdown_report") assert json_path.exists() assert md_path.exists() + assert json_path.parent == tmp_path assert "Extraction Queue Manifest" in md_path.read_text(encoding="utf-8") -def test_extraction_results_extract_pdf_and_docx_without_storing_text() -> None: - report = _run_manifest("--scope", "extraction-results") +def test_extraction_results_extract_pdf_and_docx_without_storing_text(tmp_path: Path) -> None: + report = _run_manifest("--scope", "extraction-results", "--output-dir", str(tmp_path)) assert report["scope"] == "extraction-results" assert report["status"] == "pass" @@ -218,15 +226,16 @@ def test_extraction_results_extract_pdf_and_docx_without_storing_text() -> None: assert item["extraction_result"] == "ocr_blocked_missing_engine" assert item["ocr_engine_available"] is False - json_path = ROOT / report["artifacts"]["json_report"] - md_path = ROOT / report["artifacts"]["markdown_report"] + json_path = _saved(report, "json_report") + md_path = _saved(report, "markdown_report") assert json_path.exists() assert md_path.exists() + assert json_path.parent == tmp_path assert "Extraction Results Manifest" in md_path.read_text(encoding="utf-8") -def test_extraction_review_grades_extracted_sources_without_promoting_runtime_truth() -> None: - report = _run_manifest("--scope", "extraction-review") +def test_extraction_review_grades_extracted_sources_without_promoting_runtime_truth(tmp_path: Path) -> None: + report = _run_manifest("--scope", "extraction-review", "--output-dir", str(tmp_path)) assert report["scope"] == "extraction-review" assert report["status"] == "pass" @@ -250,15 +259,16 @@ def test_extraction_review_grades_extracted_sources_without_promoting_runtime_tr assert "text" not in item assert "text_preview" not in item - json_path = ROOT / report["artifacts"]["json_report"] - md_path = ROOT / report["artifacts"]["markdown_report"] + json_path = _saved(report, "json_report") + md_path = _saved(report, "markdown_report") assert json_path.exists() assert md_path.exists() + assert json_path.parent == tmp_path assert "Extraction Review Manifest" in md_path.read_text(encoding="utf-8") -def test_source_grading_batch1_classifies_promote_review_candidates_without_runtime_promotion() -> None: - report = _run_manifest("--scope", "source-grading-batch1") +def test_source_grading_batch1_classifies_promote_review_candidates_without_runtime_promotion(tmp_path: Path) -> None: + report = _run_manifest("--scope", "source-grading-batch1", "--output-dir", str(tmp_path)) assert report["scope"] == "source-grading-batch1" assert report["status"] == "pass" @@ -299,8 +309,9 @@ def test_source_grading_batch1_classifies_promote_review_candidates_without_runt assert "text" not in item assert "text_preview" not in item - json_path = ROOT / report["artifacts"]["json_report"] - md_path = ROOT / report["artifacts"]["markdown_report"] + json_path = _saved(report, "json_report") + md_path = _saved(report, "markdown_report") assert json_path.exists() assert md_path.exists() + assert json_path.parent == tmp_path assert "Source Grading Batch 1 Manifest" in md_path.read_text(encoding="utf-8") diff --git a/tests/test_hard_gap_source_hunt_2026_07_23.py b/tests/test_hard_gap_source_hunt_2026_07_23.py index 54dfac59..690c54cf 100644 --- a/tests/test_hard_gap_source_hunt_2026_07_23.py +++ b/tests/test_hard_gap_source_hunt_2026_07_23.py @@ -3,7 +3,7 @@ from pathlib import Path ROOT=Path(__file__).resolve().parents[1] PACKET=ROOT/'references/oracle/hard_gap_source_hunt_2026_07_23.json' def test_hard_gap_source_hunt_does_not_upgrade_oracles(): - data=json.loads(subprocess.check_output(['python3','scripts/hard_gap_source_hunt_2026_07_23.py'],cwd=ROOT,text=True)) + data=json.loads(subprocess.check_output(['python3','scripts/hard_gap_source_hunt_2026_07_23.py','--no-write'],cwd=ROOT,text=True)) assert data['claim_status']=='source_hunt_only' assert data['truth_matrix_allowed'] is False assert data['summary']['ready_numeric_oracle_count']==0 diff --git a/tests/test_numeric_oracle_gap_queue_v3.py b/tests/test_numeric_oracle_gap_queue_v3.py index 5ab403a1..ac7cb27e 100644 --- a/tests/test_numeric_oracle_gap_queue_v3.py +++ b/tests/test_numeric_oracle_gap_queue_v3.py @@ -3,7 +3,7 @@ from pathlib import Path ROOT=Path(__file__).resolve().parents[1] PACKET=ROOT/'references/oracle/numeric_oracle_gap_queue_v3_2026_07_23.json' def test_numeric_oracle_gap_queue_v3_blocks_truth_upgrade(): - data=json.loads(subprocess.check_output(['python3','scripts/numeric_oracle_gap_queue_v3.py'],cwd=ROOT,text=True)) + data=json.loads(subprocess.check_output(['python3','scripts/numeric_oracle_gap_queue_v3.py','--no-write'],cwd=ROOT,text=True)) assert data['claim_status']=='candidate_queue' assert data['truth_matrix_allowed'] is False assert data['summary']['numeric_packet_ready_count']==0 diff --git a/tests/test_remote_repo_visibility_check.py b/tests/test_remote_repo_visibility_check.py index d4ba2b8f..7454f942 100644 --- a/tests/test_remote_repo_visibility_check.py +++ b/tests/test_remote_repo_visibility_check.py @@ -1,6 +1,12 @@ from __future__ import annotations -from scripts.remote_repo_visibility_check import github_slug_from_remote_url, parse_ls_remote +import json + +from scripts.remote_repo_visibility_check import ( + build_report, + github_slug_from_remote_url, + parse_ls_remote, +) def test_github_slug_from_remote_url_accepts_ssh_and_https() -> None: @@ -23,3 +29,40 @@ def test_parse_ls_remote_separates_heads_and_tags() -> None: assert parsed["heads"]["codex/release-hygiene-ci"] == "def456" assert parsed["tags"]["v1.0.0"] == "aaa111" assert parsed["ref_count"] == 3 + + +def test_gitea_remote_reports_provider_and_does_not_fall_back_to_legacy_slug(monkeypatch) -> None: + from scripts import remote_repo_visibility_check as mod + + def fake_run_command(args: list[str], timeout: int) -> dict: + joined = " ".join(args) + if args[:3] == ["git", "rev-parse", "--abbrev-ref"]: + return {"ok": True, "returncode": 0, "stdout": "staging\n", "stderr": "", "error": ""} + if args[:2] == ["git", "rev-parse"]: + return {"ok": True, "returncode": 0, "stdout": "abc123\n", "stderr": "", "error": ""} + if args[:3] == ["git", "remote", "get-url"]: + return { + "ok": True, + "returncode": 0, + "stdout": "https://git.copse.top/root/Jyotisha.git\n", + "stderr": "", + "error": "", + } + if args[:2] == ["git", "ls-remote"]: + return { + "ok": True, + "returncode": 0, + "stdout": "abc123 refs/heads/main\ndef456 refs/heads/staging\n", + "stderr": "", + "error": "", + } + raise AssertionError(f"unexpected command: {joined}") + + monkeypatch.setattr(mod, "run_command", fake_run_command) + report = build_report(8) + dumped = json.dumps(report) + assert report["provider"] == "gitea" + assert report["github_slug"] is None + assert report["status"] == "verified" + assert "732642856/yinduzhanxing" not in dumped + assert report["github_api"]["error"] == "not_github_remote" diff --git a/tests/test_skill_release_clean_trial.py b/tests/test_skill_release_clean_trial.py index 2cfc76d8..c9d87694 100644 --- a/tests/test_skill_release_clean_trial.py +++ b/tests/test_skill_release_clean_trial.py @@ -68,5 +68,6 @@ def test_premium_skill_zip_runs_from_clean_directory(tmp_path: Path) -> None: ) assert user_acceptance.returncode == 0, user_acceptance.stderr or user_acceptance.stdout report = json.loads(user_acceptance.stdout) - assert report["status"] == "pass" + # 原值=status 必须 pass;新值=pass 或 partial;原因=打包目录里缺 PyJHora 时应降级而不是 fail + assert report["status"] in {"pass", "partial"} assert report["checks"]["guided_topics_entrypoint"] is True diff --git a/tests/test_skill_release_manifest.py b/tests/test_skill_release_manifest.py index 54a45884..c8545ba5 100644 --- a/tests/test_skill_release_manifest.py +++ b/tests/test_skill_release_manifest.py @@ -13,7 +13,8 @@ def test_skill_release_manifest_defines_basic_and_premium_boundaries() -> None: assert set(editions) == {"basic_git", "premium_cloud_drive"} assert editions["basic_git"]["distribution"] == "public_git_repository" assert editions["premium_cloud_drive"]["distribution"] == "cloud_drive_zip" - assert "https://github.com/732642856/yinduzhanxing" in editions["basic_git"]["source"] + # 原值=上游研究仓 GitHub 地址;新值=本产品 Gitea 仓;原因=发行清单 source 必须指向产品仓 + assert "https://git.copse.top/root/Jyotisha" in editions["basic_git"]["source"] assert any("scripts/user_invocation_acceptance_check.py" in command for command in report["acceptance_commands"]) assert any("scripts/public_release_privacy_scan.py" in command for command in report["acceptance_commands"]) assert report["privacy_boundary"]["private_birth_data_allowed"] is False diff --git a/tests/test_skill_release_package.py b/tests/test_skill_release_package.py index 2c413047..2e871bb5 100644 --- a/tests/test_skill_release_package.py +++ b/tests/test_skill_release_package.py @@ -56,7 +56,8 @@ def test_skill_release_package_can_write_zip(tmp_path) -> None: guided = archive.read("GUIDED_ENTRYPOINT.md").decode("utf-8") acceptance = json.loads(archive.read("PACKAGE_ACCEPTANCE.json").decode("utf-8")) assert "python3 scripts/user_invocation_acceptance_check.py" in install - assert "https://github.com/732642856/yinduzhanxing" in install + # 原值=上游研究仓 GitHub 地址;新值=本产品 Gitea 仓;原因=安装说明必须指向可克隆的产品仓 + assert "https://git.copse.top/root/Jyotisha" in install assert "guided_topics" in prompts assert "official_blocked" in prompts assert "western_oracle_payload" in prompts diff --git a/tests/test_user_invocation_acceptance_contract.py b/tests/test_user_invocation_acceptance_contract.py index b0f1e9be..4acba483 100644 --- a/tests/test_user_invocation_acceptance_contract.py +++ b/tests/test_user_invocation_acceptance_contract.py @@ -148,8 +148,23 @@ def test_one_command_user_invocation_acceptance_check() -> None: assert completed.returncode == 0, completed.stderr or completed.stdout report = json.loads(completed.stdout) - assert report["status"] == "pass" + # 原值=status 必须 pass;新值=pass 或 partial;原因=PyJHora 缺席是外部参照降级,不应把验收检查打成 fail + assert report["status"] in {"pass", "partial"} assert report["checks"]["user_invocation_tests"] is True assert report["checks"]["guided_topics_entrypoint"] is True assert report["checks"]["external_adapter_diagnostics"] is True assert report["external_adapter_status"] in {"pass", "partial", "complete"} + + +def test_missing_reference_engine_is_partial_not_fail() -> None: + from scripts.user_invocation_acceptance_check import resolve_acceptance_status + + assert resolve_acceptance_status([], {"engines": {"PyJHora/JHora": {"status": "available"}}}) == "pass" + assert ( + resolve_acceptance_status( + [], + {"engines": {"PyJHora/JHora": {"missing_dependency": "pyjhora"}}}, + ) + == "partial" + ) + assert resolve_acceptance_status(["user_invocation_pytest_failed"], {"engines": {}}) == "fail"