Compare commits

..

No commits in common. "eaa061fd2b69c2997cc5247d80710605f1820d05" and "c48354e0cbd3ae8cd1df0a97d6400c835d378e7a" have entirely different histories.

78 changed files with 529 additions and 2926 deletions

View File

@ -30,7 +30,6 @@ for entry in data["sources"]:
entry.get("template_root", ""), entry.get("template_root", ""),
entry.get("data_dir", ""), entry.get("data_dir", ""),
entry.get("scripts_dir", ""), entry.get("scripts_dir", ""),
"\x1e".join(entry.get("include_skill_dirs", [])),
] ]
) )
) )
@ -106,24 +105,6 @@ tracked_skill_exists() {
[ -f "skills/$name/SKILL.md" ] [ -f "skills/$name/SKILL.md" ]
} }
skill_dir_included() {
local name="$1"
local include_skill_dirs="$2"
[ -n "$include_skill_dirs" ] || return 0
local IFS=$'\x1e'
local expected
read -r -a included <<< "$include_skill_dirs"
for expected in "${included[@]}"; do
if [ "$name" = "$expected" ]; then
return 0
fi
done
return 1
}
cd "$REPO_DIR" cd "$REPO_DIR"
@ -149,7 +130,7 @@ if ! emit_sources_tsv > "$sources_file"; then
exit 1 exit 1
fi fi
while IFS=$'\x1f' read -r source_id snapshot_dir sync_mode source_list skills_subdir output_name platform_config template_root data_dir scripts_dir include_skill_dirs; do while IFS=$'\x1f' read -r source_id snapshot_dir sync_mode source_list skills_subdir output_name platform_config template_root data_dir scripts_dir; do
[ -n "$source_id" ] || continue [ -n "$source_id" ] || continue
if [ -f "$source_list" ]; then if [ -f "$source_list" ]; then
@ -161,7 +142,7 @@ while IFS=$'\x1f' read -r source_id snapshot_dir sync_mode source_list skills_su
done < "$sources_file" done < "$sources_file"
declare -A owners=() declare -A owners=()
while IFS=$'\x1f' read -r source_id snapshot_dir sync_mode source_list skills_subdir output_name platform_config template_root data_dir scripts_dir include_skill_dirs; do while IFS=$'\x1f' read -r source_id snapshot_dir sync_mode source_list skills_subdir output_name platform_config template_root data_dir scripts_dir; do
[ -n "$source_id" ] || continue [ -n "$source_id" ] || continue
git archive --format=tar "origin/${THIRDPARTY_BRANCH}" "$snapshot_dir" | tar -xf - -C "$tmp_dir" git archive --format=tar "origin/${THIRDPARTY_BRANCH}" "$snapshot_dir" | tar -xf - -C "$tmp_dir"
@ -179,9 +160,6 @@ while IFS=$'\x1f' read -r source_id snapshot_dir sync_mode source_list skills_su
for dir in "$source_skills_dir"/*; do for dir in "$source_skills_dir"/*; do
[ -d "$dir" ] || continue [ -d "$dir" ] || continue
name="$(basename "$dir")" name="$(basename "$dir")"
if ! skill_dir_included "$name" "$include_skill_dirs"; then
continue
fi
if [ -n "${owners[$name]:-}" ] && [ "${owners[$name]}" != "$source_id" ]; then if [ -n "${owners[$name]:-}" ] && [ "${owners[$name]}" != "$source_id" ]; then
echo "ERROR: duplicate third-party skill name: $name" >&2 echo "ERROR: duplicate third-party skill name: $name" >&2
exit 1 exit 1

View File

@ -31,44 +31,6 @@
"sync_mode": "copy_skill_dirs", "sync_mode": "copy_skill_dirs",
"source_list": "skills/thirdparty/.sources/andrej-karpathy-skills.list", "source_list": "skills/thirdparty/.sources/andrej-karpathy-skills.list",
"skills_subdir": "skills" "skills_subdir": "skills"
},
{
"id": "brooks-lint",
"upstream_repo": "https://github.com/hyhmrright/brooks-lint.git",
"upstream_ref": "main",
"snapshot_dir": "brooks-lint",
"sync_mode": "copy_skill_dirs",
"source_list": "skills/thirdparty/.sources/brooks-lint.list",
"skills_subdir": "skills",
"include_skill_dirs": [
"_shared",
"brooks-audit",
"brooks-debt",
"brooks-health",
"brooks-review",
"brooks-sweep",
"brooks-test"
]
},
{
"id": "codebase-recon",
"upstream_repo": "https://github.com/outfitter-dev/agents.git",
"upstream_ref": "main",
"snapshot_dir": "outfitter-agents",
"sync_mode": "copy_skill_dirs",
"source_list": "skills/thirdparty/.sources/codebase-recon.list",
"skills_subdir": "plugins/outfitter/skills",
"include_skill_dirs": ["codebase-recon"]
},
{
"id": "codebase-migrate",
"upstream_repo": "https://github.com/ComposioHQ/awesome-codex-skills.git",
"upstream_ref": "main",
"snapshot_dir": "awesome-codex-skills",
"sync_mode": "copy_skill_dirs",
"source_list": "skills/thirdparty/.sources/codebase-migrate.list",
"skills_subdir": ".",
"include_skill_dirs": ["codebase-migrate"]
} }
] ]
} }

View File

@ -127,6 +127,7 @@ Layer 1: rulesets/ (≤50 行/语言,模板源)
Layer 2: skills/ (按需加载,$skill-name 触发) Layer 2: skills/ (按需加载,$skill-name 触发)
├─ commit-message: 提交信息规范 ├─ commit-message: 提交信息规范
├─ style-cleanup: 代码风格整理 ├─ style-cleanup: 代码风格整理
├─ bulk-refactor-workflow: 批量重构流程
└─ thirdparty/: 第三方同步 skills └─ thirdparty/: 第三方同步 skills
Layer 3: docs/ (权威静态文档) Layer 3: docs/ (权威静态文档)
@ -172,8 +173,8 @@ TSL 相关问题直接查阅 `rulesets/tsl/index.md` 与 `docs/tsl/`。
**通用 Skills** **通用 Skills**
- `$commit-message`:提交信息规范 - `$commit-message`:提交信息规范
- `$gitea-fix-ci`:诊断 Gitea Actions / PR checks 失败并按批准计划修复
- `$style-cleanup`:整理代码风格 - `$style-cleanup`:整理代码风格
- `$bulk-refactor-workflow`:批量重构流程
- 更多见 `SKILLS.md` - 更多见 `SKILLS.md`
**安装与使用**:详见 `SKILLS.md` **安装与使用**:详见 `SKILLS.md`

View File

@ -174,8 +174,8 @@ python <deploy_root>/scripts/playbook.py -config playbook.toml
### 通用工作流 Skills ### 通用工作流 Skills
- `commit-message`:基于 staged diff 自动建议提交信息(`:emoji: type(scope): subject` - `commit-message`:基于 staged diff 自动建议提交信息(`:emoji: type(scope): subject`
- `gitea-fix-ci`:诊断 Gitea Actions / PR checks 失败,先提取日志证据并形成修复计划,再按批准范围改代码
- `style-cleanup`:整理代码风格(优先使用仓库既有 formatter/lint 工具链) - `style-cleanup`:整理代码风格(优先使用仓库既有 formatter/lint 工具链)
- `bulk-refactor-workflow`:批量重构(安全做法 + 验证契约)
--- ---

View File

@ -22,8 +22,8 @@
### 1.1 单一职责 ### 1.1 单一职责
- 一个文件只做一件事;职责明确。 - 一个文件只做一件事;职责明确。
- `.tsl` 作为可执行脚本:语句区在前并按顺序执行;需要本文件函数/类时,声明区放在语句区之后。 - `.tsl` 建议作为“入口/编排层”:聚合参数/配置、串起流程;可复用逻辑下沉到
- `.tsf` 作为模块/函数扩展文件:可复用逻辑下沉到 `.tsf`,部署到解释器 `funcext` 后供脚本调用 `.tsf``unit`/`class`/`function`)中
- 当一个顶层声明同时承担“协议适配 + 业务计算 + - 当一个顶层声明同时承担“协议适配 + 业务计算 +
I/O/环境依赖 + 临时代码”时,优先拆分边界:核心纯逻辑 → 工具函数 → 边界适配I/O I/O/环境依赖 + 临时代码”时,优先拆分边界:核心纯逻辑 → 工具函数 → 边界适配I/O
@ -32,7 +32,6 @@
- TSL 语法要求(仅 `.tsf`):每个 `.tsf` - TSL 语法要求(仅 `.tsf`):每个 `.tsf`
文件只能有一个顶层声明,且文件基名必须与顶层声明同名。 文件只能有一个顶层声明,且文件基名必须与顶层声明同名。
- `.tsl` 允许直接写语句,不要求顶层声明;文件名不强制,但建议清晰可检索。 - `.tsl` 允许直接写语句,不要求顶层声明;文件名不强制,但建议清晰可检索。
- `.tsl` 中如果同时需要脚本语句和函数/类声明,先写语句区,再写声明区;不要在声明区后继续追加脚本语句。
- 推荐文件名使用 `PascalCase` 以提升检索与协作一致性;扩展名按类型使用 - 推荐文件名使用 `PascalCase` 以提升检索与协作一致性;扩展名按类型使用
`.tsl`/`.tsf`(两者都属于 TSL 源文件,风格规则一致)。 `.tsl`/`.tsf`(两者都属于 TSL 源文件,风格规则一致)。
- 详细约束与命名细则见 `docs/tsl/naming.md` - 详细约束与命名细则见 `docs/tsl/naming.md`

View File

@ -2,17 +2,12 @@
文档类型:业务骨架 文档类型:业务骨架
是否可直接用于生成代码:否 是否可直接用于生成代码:否
是否含已验证可执行示例:否
是否含已验证反例:否
遇到不确定时跳转到:项目实际接口定义、[../modules/tsbacktesting.md](../modules/tsbacktesting.md)、[selection_and_signal_patterns.md](selection_and_signal_patterns.md)、[../syntax/index.md](../syntax/index.md) 遇到不确定时跳转到:项目实际接口定义、[../modules/tsbacktesting.md](../modules/tsbacktesting.md)、[selection_and_signal_patterns.md](selection_and_signal_patterns.md)、[../syntax/index.md](../syntax/index.md)
这一篇收拢回测、组合、交易与结果读取流程。 这一篇收拢回测、组合、交易与结果读取流程。
## Agent 回测/交易判断规则
- 先判断比例类组合还是数量类组合,再看交易输入和结果读取。
- 本页只给业务流程骨架,不给对象 API 真值。
- 对象创建方式、交易数据入口、结果接口和项目封装必须回项目实际接口定义。
- 不要发明项目实际接口、回测对象创建方式、交易入口或结果读取调用链。
## 这一篇解决什么问题 ## 这一篇解决什么问题
回答“回测对象如何组织、交易流程如何设置、结果怎样取出和解释”。 回答“回测对象如何组织、交易流程如何设置、结果怎样取出和解释”。

View File

@ -2,16 +2,12 @@
文档类型:检索页 文档类型:检索页
是否可直接用于生成代码:否 是否可直接用于生成代码:否
是否含已验证可执行示例:否
是否含已验证反例:否
遇到不确定时跳转到:[market_data_context.md](market_data_context.md)、[../syntax/index.md](../syntax/index.md)、[../reference/index.md](../reference/index.md) 遇到不确定时跳转到:[market_data_context.md](market_data_context.md)、[../syntax/index.md](../syntax/index.md)、[../reference/index.md](../reference/index.md)
这里是金融层的入口决策页,不是代码页。它只解决“业务任务该往哪一层跳”,不重新定义语言基础语法。 这里是金融层的入口决策页,不是代码页。它只解决“业务任务该往哪一层跳”,不重新定义语言基础语法。
## Agent Finance Entry 判断规则
- 先判断任务属于市场数据、序列指标、选股信号还是回测交易。
- finance 只给业务组织方式;代码细节必须回 syntax、reference 或项目实际接口。
- 不要发明项目实际接口、数据字段、函数签名或业务上下文。
## 这一篇解决什么问题 ## 这一篇解决什么问题
回答“什么时候应该进入 finance 层、进入后先去哪个业务主题页,以及什么时候该回到 syntax / reference 层”。 回答“什么时候应该进入 finance 层、进入后先去哪个业务主题页,以及什么时候该回到 syntax / reference 层”。

View File

@ -2,16 +2,12 @@
文档类型:检索页 文档类型:检索页
是否可直接用于生成代码:否 是否可直接用于生成代码:否
是否含已验证可执行示例:否
是否含已验证反例:否
遇到不确定时跳转到:[../syntax/index.md](../syntax/index.md)(优先)、[market_data_context.md](market_data_context.md)、[../reference/index.md](../reference/index.md) 遇到不确定时跳转到:[../syntax/index.md](../syntax/index.md)(优先)、[market_data_context.md](market_data_context.md)、[../reference/index.md](../reference/index.md)
这里是业务层入口。只讨论金融任务如何使用 TSL不重讲基础语法。 这里是业务层入口。只讨论金融任务如何使用 TSL不重讲基础语法。
## Agent Finance 路由规则
- 只用 finance 判断金融业务任务该怎么分层,不用 finance 重写语法规则。
- 金融任务需要真实数据字段、回测接口或项目封装时,先回项目实际接口定义。
- 不要发明项目实际接口、字段名、对象创建方式或函数签名。
## 先看这 5 条 ## 先看这 5 条
- 如果你的问题是“语言怎么写”,不要留在 finance回到 [../syntax/index.md](../syntax/index.md)。 - 如果你的问题是“语言怎么写”,不要留在 finance回到 [../syntax/index.md](../syntax/index.md)。

View File

@ -2,19 +2,14 @@
文档类型:业务骨架 文档类型:业务骨架
是否可直接用于生成代码:否 是否可直接用于生成代码:否
是否含已验证可执行示例:否
是否含已验证反例:否
遇到不确定时跳转到:[series_and_indicator_model.md](series_and_indicator_model.md)、[../syntax/index.md](../syntax/index.md)、[../modules/tsbacktesting.md](../modules/tsbacktesting.md) 遇到不确定时跳转到:[series_and_indicator_model.md](series_and_indicator_model.md)、[../syntax/index.md](../syntax/index.md)、[../modules/tsbacktesting.md](../modules/tsbacktesting.md)
本页用于判断金融脚本运行时的数据语境,不提供独立语法模板。 本页用于判断金融脚本运行时的数据语境,不提供独立语法模板。
这一篇整理金融场景中的市场数据语境与执行环境。 这一篇整理金融场景中的市场数据语境与执行环境。
## Agent 市场数据判断规则
- 先确认脚本运行时是否已经有市场数据上下文。
- 只把本页用于判断业务语境,不把市场字段当成 TSL 通用语法。
- 如果字段来源、周期、复权口径或数据入口不明确,回项目实际接口定义。
- 不要发明项目实际接口、市场字段或默认数据上下文。
## 这一篇解决什么问题 ## 这一篇解决什么问题
回答“金融脚本运行时的数据上下文是什么、哪些概念属于业务层而不是语言层”。 回答“金融脚本运行时的数据上下文是什么、哪些概念属于业务层而不是语言层”。

View File

@ -2,17 +2,12 @@
文档类型:业务骨架 文档类型:业务骨架
是否可直接用于生成代码:否 是否可直接用于生成代码:否
是否含已验证可执行示例:否
是否含已验证反例:否
遇到不确定时跳转到:[series_and_indicator_model.md](series_and_indicator_model.md)、[backtest_and_trade_flow.md](backtest_and_trade_flow.md)、[../syntax/index.md](../syntax/index.md) 遇到不确定时跳转到:[series_and_indicator_model.md](series_and_indicator_model.md)、[backtest_and_trade_flow.md](backtest_and_trade_flow.md)、[../syntax/index.md](../syntax/index.md)
这一篇收拢选股、筛选和信号生成的业务模式。 这一篇收拢选股、筛选和信号生成的业务模式。
## Agent 选股/信号判断规则
- 先确认任务输出是筛选结果、交易信号、评分排序还是回测输入。
- 条件表达的语法回 syntax金融任务组织留在 finance。
- 需要真实字段、信号函数或输出接口时,回项目实际接口定义。
- 不要发明项目实际接口、字段名、信号函数签名或输出结构。
## 这一篇解决什么问题 ## 这一篇解决什么问题
回答“如何把条件表达和金融筛选任务组织成稳定的选股/信号脚本”。 回答“如何把条件表达和金融筛选任务组织成稳定的选股/信号脚本”。

View File

@ -2,17 +2,12 @@
文档类型:业务骨架 文档类型:业务骨架
是否可直接用于生成代码:否 是否可直接用于生成代码:否
是否含已验证可执行示例:否
是否含已验证反例:否
遇到不确定时跳转到:[market_data_context.md](market_data_context.md)、[selection_and_signal_patterns.md](selection_and_signal_patterns.md)、[../syntax/index.md](../syntax/index.md) 遇到不确定时跳转到:[market_data_context.md](market_data_context.md)、[selection_and_signal_patterns.md](selection_and_signal_patterns.md)、[../syntax/index.md](../syntax/index.md)
这一篇处理金融序列与指标计算模式。 这一篇处理金融序列与指标计算模式。
## Agent 序列/指标判断规则
- 先确认输入是单值还是逐 bar 序列。
- 指标公式只在业务上下文里成立,不替代 syntax 的表达式规则。
- 需要真实字段、指标函数或窗口口径时,回项目实际接口定义或 reference 参数事实页。
- 不要发明项目实际接口、字段名、指标函数签名或历史窗口语义。
## 这一篇解决什么问题 ## 这一篇解决什么问题
回答“指标、序列、逐 bar 计算和相关金融表达方式如何组织,以及 AI 应该先建立什么样的业务心智模型”。 回答“指标、序列、逐 bar 计算和相关金融表达方式如何组织,以及 AI 应该先建立什么样的业务心智模型”。

View File

@ -6,26 +6,22 @@
是否含已验证反例:否 是否含已验证反例:否
遇到不确定时跳转到:[syntax/index.md](syntax/index.md)、[finance/index.md](finance/index.md)、[reference/index.md](reference/index.md)、[modules/index.md](modules/index.md)、项目自身文档、`scripts/*` 入口脚本、CI 配置 遇到不确定时跳转到:[syntax/index.md](syntax/index.md)、[finance/index.md](finance/index.md)、[reference/index.md](reference/index.md)、[modules/index.md](modules/index.md)、项目自身文档、`scripts/*` 入口脚本、CI 配置
这个入口文件只负责一件事:让 agent 先判断主问题属于哪一层,再进入最相关的单个入口页。这里的语法文档面向 agent 决策,不按人类教程组织;回答和生成代码时必须按流程读,不要凭语言相似性补全。 这个入口文件只负责一件事:让新 session 先判断主问题属于哪一层,再进入最相关的单个入口页。
## 先记住这些规则 ## 先记住这些规则
- 先读本文件,不要默认通读全部 TSL 文档。 - 先读本文件,不要默认通读全部 TSL 文档。
- 用户已给出 `.tsl` / `.tsf` 后缀时,后缀就是判断依据;未给后缀时,再根据用户交付目标判断。
- `.tsl` 是可执行脚本;`.tsf` 是部署到解释器 `funcext` 的模块/函数扩展文件。
- 写 `.tsl` 时按两段理解:语句区在前并按顺序执行;函数/类声明区在后,供前面的语句调用或运行时解析。
- 写 `.tsf` 时按模块/扩展理解;顶层函数部署到 `funcext` 后,`.tsl` 脚本可以直接调用。
- 语言怎么写的问题,先从 `docs/tsl/syntax/` 开始。 - 语言怎么写的问题,先从 `docs/tsl/syntax/` 开始。
- 指标、选股、回测和策略流程的问题,先从 `docs/tsl/finance/` 开始;不要先把业务问题拆成纯语法问题。 - 指标、选股、回测和策略流程的问题,先从 `docs/tsl/finance/` 开始;不要先把业务问题拆成纯语法问题。
- 某个函数怎么用、属于哪个函数库分类或目录,先从 `docs/tsl/reference/` 开始。 - 某个函数怎么用、属于哪个函数库分类或目录,先从 `docs/tsl/reference/` 开始。
- 现成模块、外部集成和互操作问题,先从 [modules/index.md](modules/index.md) 开始。 - 现成模块、外部集成和互操作问题,先从 [modules/index.md](modules/index.md) 开始。
- 账户体系、真实接口名、部署方式、脚本入口、权限模型、环境变量、CI、验证命令这类问题先按“项目依赖 / 项目执行”处理;优先回项目自身文档、`scripts/*` 入口脚本、CI 配置。 - 账户体系、真实接口名、部署方式、脚本入口、权限模型、环境变量、CI、验证命令这类问题先按“项目依赖 / 项目执行”处理;优先回项目自身文档、`scripts/*` 入口脚本、CI 配置。
- [toolchain.md](toolchain.md) 不是 TSL 语法子类,而是项目执行类辅证页;只有当前项目已经补齐工具链与验证信息时才使用;如果这页仍是模板,不把它当主入口。 - [toolchain.md](toolchain.md) 不是 TSL 语法子类,而是项目执行类辅证页;只有当前项目已经补齐工具链与验证信息时才使用;如果这页仍是模板,不把它当主入口。
- 生成 `.tsl` 代码时,优先从脚本语句区开始;如果关键词要求函数或类,把声明放在语句区之后 - 顶层主体优先按四类理解:松散语句、`function / procedure`、`type Name = class`、`unit`
- 不要`.tsl` 的函数/类声明区之后再追加新的脚本语句 - 不要把顶层 `function / procedure` 定义和松散语句混在同一个文件模型里
- 任何语法判断都先看正式语法页结论。 - 任何语法判断都先看正式语法页结论。
- 如果涉及较新写法、资料冲突或解释器差异,先回到 `docs/tsl/syntax/index.md`,再按主题跳到对应语法页;对应主题页仍然没有结论时,不要自行补语法 - 如果涉及较新写法、资料冲突或解释器差异,先回到 `docs/tsl/syntax/index.md`,再按主题跳到对应语法页;只有对应主题页仍然没有结论时,才本地用 `tsl` 验证
- 如果涉及高频误写、反例或负向边界,优先回到 `docs/tsl/syntax/12_pitfalls.md`结论缺失时不要把猜测写成语法事实 - 如果涉及高频误写、反例或负向边界,优先回到 `docs/tsl/syntax/12_pitfalls.md`只有结论缺失时,才本地用 `tsl` 验证
- 模板、错误示例和输出片段不算可独立编译代码。 - 模板、错误示例和输出片段不算可独立编译代码。
## 元数据与证据标签 ## 元数据与证据标签
@ -38,27 +34,8 @@
- 如果还需要补充用途、限制或复用建议,单独写 `代码块说明`,不要把说明文字继续拼进 `代码块身份` - 如果还需要补充用途、限制或复用建议,单独写 `代码块说明`,不要把说明文字继续拼进 `代码块身份`
- 如果页头里的 `遇到不确定时跳转到` 列出多个目标,默认第一项是优先入口,后面的目标只用于分流或补证。 - 如果页头里的 `遇到不确定时跳转到` 列出多个目标,默认第一项是优先入口,后面的目标只用于分流或补证。
## 维护者入文档前验证
- TSL 语法页面向 agent 生成代码;所有写成语法事实的代码库样例,必须先由维护者在项目验证环境中确认。
- 正向代码库样例只有在环境验证通过后,才能标成 `已验证可执行示例`;对应输出只能标成 `已验证输出片段`
- 负向代码库样例只有在环境中确认失败形态后,才能标成 `反例 / 不可照写`
- 未经过环境验证的写法不能写成语法事实;只能作为文档缺口、待验证项,或标成 `配置片段 / 概念骨架`
- 验证过程不写进语法页解释器路径、Docker、Windows、Ubuntu、环境变量和执行命令属于项目执行层不能混入通用语法事实。
- agent 不需要自行执行验证agent 只根据维护者已经写入文档的证据标签生成代码,遇到未覆盖写法时不要发明语法。
## 新 session 起手规则 ## 新 session 起手规则
### Agent 读取流程
1. 识别目标层:语法、金融业务、函数库、模块/集成、项目执行。
2. 识别用户指定的文件后缀;用户已给出 `.tsl` / `.tsf` 后缀时,后缀就是判断依据。
3. 用户未给后缀时,按交付目标判断:可执行代码对应 `.tsl`,通用模块对应 `.tsf`;仍不明确时向用户确认。
4. 对 `.tsl`,先生成语句区;需要本文件函数/类时,再生成声明区。
5. 对 `.tsf`,生成模块/扩展声明;部署后的顶层函数可由脚本直接调用。
6. 只读取当前任务命中的单个入口页;不要同时展开语法、业务、函数库和工具链。
7. 生成代码前找 `代码块身份:已验证可执行示例`;遇到 `反例 / 不可照写` 必须避开。
### If / Then 路由 ### If / Then 路由
- If 问题在问“语言怎么写”then 先从 [syntax/index.md](syntax/index.md) 开始。 - If 问题在问“语言怎么写”then 先从 [syntax/index.md](syntax/index.md) 开始。
@ -80,17 +57,17 @@
### 语言事实 ### 语言事实
- 可以先把 TSL 当成 Pascal 风格语言去理解:`function`、`begin`、`end`、`unit`、`uses` 都很接近;但这里只借外形,不默认继承 Pascal 的全部语义、库习惯和文件模型。 - 可以先把 TSL 当成 Pascal 风格语言去理解:`function`、`begin`、`end`、`unit`、`uses` 都很接近;但这里只借外形,不默认继承 Pascal 的全部语义、库习惯和文件模型。
- 涉及赋值、`.tsl` 语句区 / 声明区、`.tsf` 模块、`function / procedure` 外形、`unit` 骨架、命名参数、`type Name = class`、数组 / 字符串下标这类高频硬规则,统一以 [syntax/02_quickstart.md](syntax/02_quickstart.md) 的“语言核心事实速查”为准;当前页只保留跨层路由所需的最小提醒。 - 涉及赋值、`function / procedure` 外形、`unit` 骨架、命名参数、`type Name = class`、数组 / 字符串下标这类高频硬规则,统一以 [syntax/02_quickstart.md](syntax/02_quickstart.md) 的“语言核心事实速查”为准;当前页只保留跨层路由所需的最小提醒。
#### 写代码前先记住 #### 写代码前先记住
- 写代码前先把高频硬规则收口到 [syntax/02_quickstart.md](syntax/02_quickstart.md),不要分别从入口页、介绍页和文件模型页拼接结论。 - 写代码前先把高频硬规则收口到 [syntax/02_quickstart.md](syntax/02_quickstart.md),不要分别从入口页、介绍页和文件模型页拼接结论。
- `.tsl` 仍优先按“语句区在前、声明区在后”的脚本模型理解;`.tsf` 仍优先按部署到 `funcext` 的模块/扩展理解 - 顶层主体仍优先按四类理解:松散语句、`function / procedure`、`type Name = class`、`unit`
- 模板、错误示例和输出片段不算可独立编译代码;真正落代码时优先看块级 `代码块身份` - 模板、错误示例和输出片段不算可独立编译代码;真正落代码时优先看块级 `代码块身份`
### 手册建模规则 ### 手册建模规则
- 更可靠的识别方式是同时任务目标和顶层内容,而不是只看文件扩展名。 - 更可靠的识别方式是看顶层内容,而不是只看文件扩展名。
- 顶层允许出现 `uses`,但这里只把它当辅助语句,不把它当主体声明。 - 顶层允许出现 `uses`,但这里只把它当辅助语句,不把它当主体声明。
- 下游大量 `program test; begin ... end.` 形式,只作为自包含验证样例外壳,不作为这里归纳的正式顶层模型。 - 下游大量 `program test; begin ... end.` 形式,只作为自包含验证样例外壳,不作为这里归纳的正式顶层模型。
@ -131,4 +108,4 @@
4. 当前页如果已经给出结论,先采用;准备编写时优先找已验证正例,再落代码;只有需要补充时再跳到相邻页。 4. 当前页如果已经给出结论,先采用;准备编写时优先找已验证正例,再落代码;只有需要补充时再跳到相邻页。
5. 遇到“资料写法不一致”且偏较新写法、资料冲突或解释器差异时,先回 [syntax/index.md](syntax/index.md) 按主题跳到对应语法页。 5. 遇到“资料写法不一致”且偏较新写法、资料冲突或解释器差异时,先回 [syntax/index.md](syntax/index.md) 按主题跳到对应语法页。
6. 遇到“资料写法不一致”且偏高频误写、反例或负向边界时,先看 [syntax/12_pitfalls.md](syntax/12_pitfalls.md)。 6. 遇到“资料写法不一致”且偏高频误写、反例或负向边界时,先看 [syntax/12_pitfalls.md](syntax/12_pitfalls.md)。
7. 如果当前手册没有给出结论,不要发明语法;改为向用户确认、记录文档缺口,或等待维护者用项目环境补充已验证结论 7. 只有当前手册没有给出结论时,才写最小 `.tsl` / `.tsf` 例子并用 `tsl` 实测

View File

@ -2,18 +2,14 @@
文档类型:检索页 文档类型:检索页
是否可直接用于生成代码:否 是否可直接用于生成代码:否
是否含已验证可执行示例:否
是否含已验证反例:否
遇到不确定时跳转到:[tsbacktesting.md](tsbacktesting.md)、[tsl_python_interop.md](tsl_python_interop.md)、[../finance/index.md](../finance/index.md)、项目自身文档、`scripts/*` 入口脚本、CI 配置 遇到不确定时跳转到:[tsbacktesting.md](tsbacktesting.md)、[tsl_python_interop.md](tsl_python_interop.md)、[../finance/index.md](../finance/index.md)、项目自身文档、`scripts/*` 入口脚本、CI 配置
这里处理“现成模块、外部集成和互操作”,不处理基础语法教学,也不替代金融业务主线。 这里处理“现成模块、外部集成和互操作”,不处理基础语法教学,也不替代金融业务主线。
模块摘要页只负责确认能力边界和选路,不负责给出项目账户体系、真实接口名、部署方式、权限模型或脚本入口的真值。 模块摘要页只负责确认能力边界和选路,不负责给出项目账户体系、真实接口名、部署方式、权限模型或脚本入口的真值。
## Agent Modules 路由规则
- 先判断任务是在问模块能力、业务流程、语法规则还是项目真实接入参数。
- modules 只给集成边界和选路;真实账号、部署入口、权限模型和接口细节必须回项目文档。
- 不要发明模块专属 API 参数、项目账号来源、部署路径或脚本入口。
## If / Then 路由 ## If / Then 路由
- If 问题在问“策略回测框架怎么组织、怎么取结果”then 先读 [tsbacktesting.md](tsbacktesting.md)。 - If 问题在问“策略回测框架怎么组织、怎么取结果”then 先读 [tsbacktesting.md](tsbacktesting.md)。

View File

@ -2,18 +2,14 @@
文档类型:模块摘要 文档类型:模块摘要
是否可直接用于生成代码:仅部分 是否可直接用于生成代码:仅部分
是否含已验证可执行示例:否
是否含已验证反例:否
遇到不确定时跳转到:项目级部署文档、官方 pyTSL 详细接口文档、[tsl_python_interop.md](tsl_python_interop.md)、[index.md](index.md) 遇到不确定时跳转到:项目级部署文档、官方 pyTSL 详细接口文档、[tsl_python_interop.md](tsl_python_interop.md)、[index.md](index.md)
本页用于确认 pyTSL 的接入方向和最小链路,不替代项目级部署文档或完整接口手册。 本页用于确认 pyTSL 的接入方向和最小链路,不替代项目级部署文档或完整接口手册。
- 如果登录方式、凭证来源、环境变量、部署入口、连接上下文或返回结构没有确认,不继续生成接入代码,直接回项目级部署文档或官方 pyTSL 详细接口文档。 - 如果登录方式、凭证来源、环境变量、部署入口、连接上下文或返回结构没有确认,不继续生成接入代码,直接回项目级部署文档或官方 pyTSL 详细接口文档。
## Agent pyTSL 边界规则
- 先确认项目是否已经给出 pyTSL 安装方式、登录方式、凭证来源和返回结构。
- 本页只给 SDK 能力分类和最小链路,不替代官方接口手册。
- 不要发明登录参数、连接上下文、查询语句来源或返回数据结构。
## 定位 ## 定位
- 官方 Python SDK面向取数/执行/批量/异步与数据转换。 - 官方 Python SDK面向取数/执行/批量/异步与数据转换。
@ -77,7 +73,8 @@
示例里的 `"user"` / `"password"` 只表示调用外形,不代表项目里的真实登录方式或凭证来源。 示例里的 `"user"` / `"password"` 只表示调用外形,不代表项目里的真实登录方式或凭证来源。
下面代码只表示调用外形;真实登录方式、凭证和查询来源以项目文档为准。 代码块身份:配置片段 / 概念骨架
代码块说明:可参考最小链路,不是已验证可执行示例。
```python ```python
import pyTSL import pyTSL

View File

@ -2,18 +2,14 @@
文档类型:模块摘要 文档类型:模块摘要
是否可直接用于生成代码:否 是否可直接用于生成代码:否
是否含已验证可执行示例:否
是否含已验证反例:否
遇到不确定时跳转到:项目实际接口定义、[../finance/backtest_and_trade_flow.md](../finance/backtest_and_trade_flow.md)、[index.md](index.md) 遇到不确定时跳转到:项目实际接口定义、[../finance/backtest_and_trade_flow.md](../finance/backtest_and_trade_flow.md)、[index.md](index.md)
本页不足以直接生成回测代码,只用于确认回测任务的组织顺序,以及哪些地方必须回到项目实际接口定义继续核对。 本页不足以直接生成回测代码,只用于确认回测任务的组织顺序,以及哪些地方必须回到项目实际接口定义继续核对。
- 只要任务已经进入对象创建、交易输入入口、结果读取方法或项目封装差异,就先停止生成,直接回项目实际接口定义,不要先拼调用链。 - 只要任务已经进入对象创建、交易输入入口、结果读取方法或项目封装差异,就先停止生成,直接回项目实际接口定义,不要先拼调用链。
## Agent TSBackTesting 边界规则
- 只把本页用于识别回测框架任务顺序和常见字段类别。
- 对象创建方式、最小必填字段、交易入口和结果接口都以项目实际接口定义为准。
- 不要发明 `TSBackTesting` 构造方式、交易输入函数或结果读取调用链。
## 适用场景 ## 适用场景
- 任务已经进入“回测框架怎么配置、怎么执行、怎么读结果”。 - 任务已经进入“回测框架怎么配置、怎么执行、怎么读结果”。

View File

@ -2,18 +2,14 @@
文档类型:模块摘要 文档类型:模块摘要
是否可直接用于生成代码:否 是否可直接用于生成代码:否
是否含已验证可执行示例:否
是否含已验证反例:否
遇到不确定时跳转到:项目级部署文档、对应官方文档、[pytsl_api.md](pytsl_api.md)、[index.md](index.md) 遇到不确定时跳转到:项目级部署文档、对应官方文档、[pytsl_api.md](pytsl_api.md)、[index.md](index.md)
本页用于接入决策和最小链路确认,不替代项目级部署文档。 本页用于接入决策和最小链路确认,不替代项目级部署文档。
- 如果登录方式、凭证来源、位数、环境变量、连接通道或部署入口没有确认,不继续生成接入代码,直接回项目级部署文档或对应官方文档。 - 如果登录方式、凭证来源、位数、环境变量、连接通道或部署入口没有确认,不继续生成接入代码,直接回项目级部署文档或对应官方文档。
## Agent Python 互操作边界规则
- 先判断是 Python 调 TSL、TSL 调 Python还是服务器侧 Python 服务。
- 本页只给接入路径和接口类别;真实登录方式、位数、环境变量和部署入口必须回项目级部署文档。
- 不要发明凭证来源、连接通道、Python 路径或服务部署方式。
## 摘要 ## 摘要
- 覆盖三类交互Python 调用 TSL、TSL 调用 Python、落地服务器开启 Python 服务。 - 覆盖三类交互Python 调用 TSL、TSL 调用 Python、落地服务器开启 Python 服务。

View File

@ -2,16 +2,12 @@
文档类型:模块摘要 文档类型:模块摘要
是否可直接用于生成代码:仅部分 是否可直接用于生成代码:仅部分
是否含已验证可执行示例:否
是否含已验证反例:否
遇到不确定时跳转到:调用侧账户体系文档、项目实际接口说明、[index.md](index.md) 遇到不确定时跳转到:调用侧账户体系文档、项目实际接口说明、[index.md](index.md)
本页用于确认接口名称、参数含义和风险边界,不替代项目级接入文档。 本页用于确认接口名称、参数含义和风险边界,不替代项目级接入文档。
## Agent 微信消息边界规则
- 先确认调用侧已经提供 `userid`、模板类型、关键字含义和必要的账户字段。
- 本页只给接口外形和参数含义;账户体系、授权关系和模板映射以项目实际接口说明为准。
- 不要发明 `username` 来源、模板关键字含义、项目封装函数或账户映射。
## 适用场景 ## 适用场景
- 通过“天软科技服务号”向微信客户端发送模板消息。 - 通过“天软科技服务号”向微信客户端发送模板消息。
@ -63,7 +59,8 @@
## 发送示例(配置片段,可参考参数组织) ## 发送示例(配置片段,可参考参数组织)
下面代码只表示参数组织方式;真实账户、模板关键字和调用侧封装以项目实际接口说明为准。 代码块身份:配置片段 / 概念骨架
代码块说明:可参考参数组织,不是已验证可执行示例。
```tsl ```tsl
// 定义参数 // 定义参数

View File

@ -50,7 +50,8 @@ Guide 对齐:通过名字的“形状”快速判断实体类型(类型/函
## 3. 类型命名Type Names ## 3. 类型命名Type Names
AI 先按 `.tsl` 可执行脚本与 `.tsf` 模块/函数扩展判断文件模型。`.tsl` 里的语句区按顺序执行,函数/类声明区放在语句区之后;`.tsf` 用于可复用顶层声明并部署到解释器 `funcext`。本页只覆盖“需要命名的顶层实体”,不覆盖脚本语句本身;文件模型判断本身以 AI 先按四类顶层外形判断文件模型:顶层松散语句、顶层 `function / procedure`
顶层 `type Name = class`、顶层 `unit`。本页只覆盖“需要命名的顶层实体”,不覆盖松散语句本身;文件模型判断本身以
`docs/tsl/syntax/03_core_model.md` 为准。 `docs/tsl/syntax/03_core_model.md` 为准。
- **类与单元**使用 - **类与单元**使用
@ -75,7 +76,7 @@ TSL 的语法要求(仅 `.tsf`):每个 `.tsf`
`function / procedure`、`type Name = class`、`unit`,文件基名需与之同名。 `function / procedure`、`type Name = class`、`unit`,文件基名需与之同名。
- `.tsl` 脚本文件:用于入口/编排层;允许直接写语句(如 - `.tsl` 脚本文件:用于入口/编排层;允许直接写语句(如
`a := 1; echo a;`),也可能出现顶层 `function / procedure` 骨架或 `program test;` `a := 1; echo a;`),也可能出现顶层 `function / procedure` 骨架或 `program test;`
这类验证样例外壳;如果同时出现脚本语句和函数/类声明,语句区在前,声明区在后;但风格上不把 `.tsl` 当成可复用顶层声明的默认落点,也不要求文件基名与函数名一致;可复用逻辑优先下沉到 这类验证样例外壳;但风格上不把 `.tsl` 当成可复用顶层声明的默认落点,也不要求文件基名与函数名一致;可复用逻辑优先下沉到
`.tsf`(见 `docs/tsl/code_style.md`)。 `.tsf`(见 `docs/tsl/code_style.md`)。
- 注:`.tsf` 也是 TSL 源文件,命名/风格与 `.tsl` 遵循同一套规则。 - 注:`.tsf` 也是 TSL 源文件,命名/风格与 `.tsl` 遵循同一套规则。
- **硬规则(仅 - **硬规则(仅

View File

@ -2,12 +2,6 @@
这一页只负责函数定位:先按主题找到模块,再在页内搜索函数名。 这一页只负责函数定位:先按主题找到模块,再在页内搜索函数名。
## 候选函数索引说明
- 本页是候选函数索引,只说明函数名被归入当前模块。
- 候选名没有进入 verified 函数页前不能当成可调用事实。
- 生成代码前必须先查 [../verified/index.md](../verified/index.md);只从 verified 函数页读取参数类型。
## 使用方式 ## 使用方式
- 返回总目录:[catalog/index.md](index.md) - 返回总目录:[catalog/index.md](index.md)

View File

@ -2,12 +2,6 @@
这一页只负责函数定位:先按主题找到模块,再在页内搜索函数名。 这一页只负责函数定位:先按主题找到模块,再在页内搜索函数名。
## 候选函数索引说明
- 本页是候选函数索引,只说明函数名被归入当前模块。
- 候选名没有进入 verified 函数页前不能当成可调用事实。
- 生成代码前必须先查 [../verified/index.md](../verified/index.md);只从 verified 函数页读取参数类型。
## 使用方式 ## 使用方式
- 返回总目录:[catalog/index.md](index.md) - 返回总目录:[catalog/index.md](index.md)

View File

@ -2,12 +2,6 @@
这一页只负责函数定位:先按主题找到模块,再在页内搜索函数名。 这一页只负责函数定位:先按主题找到模块,再在页内搜索函数名。
## 候选函数索引说明
- 本页是候选函数索引,只说明函数名被归入当前模块。
- 候选名没有进入 verified 函数页前不能当成可调用事实。
- 生成代码前必须先查 [../verified/index.md](../verified/index.md);只从 verified 函数页读取参数类型。
## 使用方式 ## 使用方式
- 返回总目录:[catalog/index.md](index.md) - 返回总目录:[catalog/index.md](index.md)

View File

@ -2,12 +2,6 @@
这一页只负责函数定位:先按主题找到模块,再在页内搜索函数名。 这一页只负责函数定位:先按主题找到模块,再在页内搜索函数名。
## 候选函数索引说明
- 本页是候选函数索引,只说明函数名被归入当前模块。
- 候选名没有进入 verified 函数页前不能当成可调用事实。
- 生成代码前必须先查 [../verified/index.md](../verified/index.md);只从 verified 函数页读取参数类型。
## 使用方式 ## 使用方式
- 返回总目录:[catalog/index.md](index.md) - 返回总目录:[catalog/index.md](index.md)

View File

@ -2,12 +2,6 @@
这一页只负责函数定位:先按主题找到模块,再在页内搜索函数名。 这一页只负责函数定位:先按主题找到模块,再在页内搜索函数名。
## 候选函数索引说明
- 本页是候选函数索引,只说明函数名被归入当前模块。
- 候选名没有进入 verified 函数页前不能当成可调用事实。
- 生成代码前必须先查 [../verified/index.md](../verified/index.md);只从 verified 函数页读取参数类型。
## 使用方式 ## 使用方式
- 返回总目录:[catalog/index.md](index.md) - 返回总目录:[catalog/index.md](index.md)

View File

@ -1,17 +1,13 @@
# Function Catalog # Function Catalog
这里是候选函数索引。它只回答“函数名可能在哪个模块里”,不承担基础语法教学,也不证明函数在当前环境可用。 这里是 canonical 函数目录。它只回答“函数在哪个模块里”,不承担基础语法教学。
候选名没有进入 verified 函数页前不能当成可调用事实。agent 真正生成代码前,只从 verified 函数页读取参数类型。
## 使用顺序 ## 使用顺序
1. 不知道函数在哪个模块,先看下面的模块目录。 1. 不知道函数在哪个模块,先看下面的模块目录。
2. 进入模块页后,在页内搜索具体候选函数名。 2. 进入模块页后,在页内搜索具体函数名。
3. 如果要生成代码,先看 [../verified/index.md](../verified/index.md),再进入具体函数页;当前核心函数页是 [../verified/core.md](../verified/core.md)。 3. 如果问题是语法怎么写,回到 [../../syntax/index.md](../../syntax/index.md)。
4. 如果候选函数没有进入 verified 函数页,不要生成调用代码。 4. 如果问题是金融场景如何组织,回到 [../../finance/index.md](../../finance/index.md)。
5. 如果问题是语法怎么写,回到 [../../syntax/index.md](../../syntax/index.md)。
6. 如果问题是金融场景如何组织,回到 [../../finance/index.md](../../finance/index.md)。
## 模块目录 ## 模块目录
@ -31,11 +27,4 @@
## 说明 ## 说明
- 这套目录页由仓库内的函数语料自动整理生成。 - 这套目录页由仓库内的函数语料自动整理生成。
- catalog 只保留候选函数索引,不写参数矩阵,不写可调用结论。 - 当前目标是先提供稳定检索层,再逐步补全更细的 canonical 说明。
- 当前目标是先提供稳定检索层,再逐步补全 agent 可直接读取的参数事实。
## 候选函数索引说明
- 本页是候选函数索引,只说明函数名被归入当前模块。
- 候选名没有进入 verified 函数页前不能当成可调用事实。
- 生成代码前必须先查 [../verified/index.md](../verified/index.md);只从 verified 函数页读取参数类型。

View File

@ -2,12 +2,6 @@
这一页只负责函数定位:先按主题找到模块,再在页内搜索函数名。 这一页只负责函数定位:先按主题找到模块,再在页内搜索函数名。
## 候选函数索引说明
- 本页是候选函数索引,只说明函数名被归入当前模块。
- 候选名没有进入 verified 函数页前不能当成可调用事实。
- 生成代码前必须先查 [../verified/index.md](../verified/index.md);只从 verified 函数页读取参数类型。
## 使用方式 ## 使用方式
- 返回总目录:[catalog/index.md](index.md) - 返回总目录:[catalog/index.md](index.md)

View File

@ -2,12 +2,6 @@
这一页只负责函数定位:先按主题找到模块,再在页内搜索函数名。 这一页只负责函数定位:先按主题找到模块,再在页内搜索函数名。
## 候选函数索引说明
- 本页是候选函数索引,只说明函数名被归入当前模块。
- 候选名没有进入 verified 函数页前不能当成可调用事实。
- 生成代码前必须先查 [../verified/index.md](../verified/index.md);只从 verified 函数页读取参数类型。
## 使用方式 ## 使用方式
- 返回总目录:[catalog/index.md](index.md) - 返回总目录:[catalog/index.md](index.md)

View File

@ -2,12 +2,6 @@
这一页只负责函数定位:先按主题找到模块,再在页内搜索函数名。 这一页只负责函数定位:先按主题找到模块,再在页内搜索函数名。
## 候选函数索引说明
- 本页是候选函数索引,只说明函数名被归入当前模块。
- 候选名没有进入 verified 函数页前不能当成可调用事实。
- 生成代码前必须先查 [../verified/index.md](../verified/index.md);只从 verified 函数页读取参数类型。
## 使用方式 ## 使用方式
- 返回总目录:[catalog/index.md](index.md) - 返回总目录:[catalog/index.md](index.md)

View File

@ -2,12 +2,6 @@
这一页只负责函数定位:先按主题找到模块,再在页内搜索函数名。 这一页只负责函数定位:先按主题找到模块,再在页内搜索函数名。
## 候选函数索引说明
- 本页是候选函数索引,只说明函数名被归入当前模块。
- 候选名没有进入 verified 函数页前不能当成可调用事实。
- 生成代码前必须先查 [../verified/index.md](../verified/index.md);只从 verified 函数页读取参数类型。
## 使用方式 ## 使用方式
- 返回总目录:[catalog/index.md](index.md) - 返回总目录:[catalog/index.md](index.md)

View File

@ -2,12 +2,6 @@
这一页只负责函数定位:先按主题找到模块,再在页内搜索函数名。 这一页只负责函数定位:先按主题找到模块,再在页内搜索函数名。
## 候选函数索引说明
- 本页是候选函数索引,只说明函数名被归入当前模块。
- 候选名没有进入 verified 函数页前不能当成可调用事实。
- 生成代码前必须先查 [../verified/index.md](../verified/index.md);只从 verified 函数页读取参数类型。
## 使用方式 ## 使用方式
- 返回总目录:[catalog/index.md](index.md) - 返回总目录:[catalog/index.md](index.md)

View File

@ -2,37 +2,33 @@
文档类型:检索页 文档类型:检索页
是否可直接用于生成代码:否 是否可直接用于生成代码:否
是否含已验证可执行示例:否
是否含已验证反例:否
遇到不确定时跳转到:[catalog/index.md](catalog/index.md)、[../syntax/index.md](../syntax/index.md)、[../finance/index.md](../finance/index.md) 遇到不确定时跳转到:[catalog/index.md](catalog/index.md)、[../syntax/index.md](../syntax/index.md)、[../finance/index.md](../finance/index.md)
这里是函数查阅层,不是默认通读入口。现在这一页本身就是函数总入口,不再要求先跳到额外的中转页。 这里是函数查阅层,不是默认通读入口。现在这一页本身就是函数总入口,不再要求先跳到额外的中转页。
reference 的目标是确认每个方法的参数类型,让 agent 写代码时知道该传什么。文档只保留 agent 生成代码需要的函数事实。 ## 先看这 5 条
## Agent 函数使用规则 - 这里用于函数查找,不用于建立基础语法模型。
- 不要把 reference 当成默认通读入口;如果任务就是查函数,可以直接从这里开始。
- 只从 verified 函数页读取参数类型、返回值和调用约束。 - 函数库规模很大,应先走索引,再做定向检索。
- catalog 只是候选函数索引,不能把 catalog 里的函数名直接当成可调用事实。 - 如果目录和定向检索都找不到函数,不要默认它是 TSL 内建函数;先回语法层、业务层、模块页或项目文档确认来源。
- 函数没有进入 verified 函数页时,不要根据函数名猜参数,也不要生成调用代码。 - 如果你还在问“语言怎么写”,先回 [../syntax/index.md](../syntax/index.md)。
- 函数参数必须按正确类型传入TSL 本身弱类型,不等于函数参数无类型。
- 如果函数被记录为当前测试环境不支持,不要使用该函数生成代码。
## 检索策略 ## 检索策略
1. 先看 [catalog/index.md](catalog/index.md) 的模块目录,确定候选大类。 1. 先看 [catalog/index.md](catalog/index.md) 的模块目录,确定大类。
2. 进入对应模块页,在页内搜索具体候选函数名。 2. 进入对应模块页,在页内搜索具体函数名。
3. 先看 [verified/index.md](verified/index.md),再进入具体 verified 函数页读取参数类型、返回值和调用约束;当前核心函数页是 [verified/core.md](verified/core.md)。 3. 仍然不确定时,再用 `rg``docs/tsl/reference/catalog/` 做定向搜索。
4. 如果候选函数没有进入 verified 函数页,不要生成调用代码。 4. 如果目录和定向检索都找不到函数,不要发明函数名,也不要默认它是 TSL 内建函数;回 [../syntax/index.md](../syntax/index.md)、[../finance/index.md](../finance/index.md)、[../modules/index.md](../modules/index.md) 或项目文档确认来源。
5. 当前测试环境不支持的方法见 [unavailable_methods.md](unavailable_methods.md)。
6. 如果目录和定向检索都找不到函数,不要发明函数名,也不要默认它是 TSL 内建函数;回 [../syntax/index.md](../syntax/index.md)、[../finance/index.md](../finance/index.md)、[../modules/index.md](../modules/index.md) 或项目文档确认来源。
## 按任务跳转 ## 按任务跳转
| 当前任务 | 先读哪里 | | 当前任务 | 先读哪里 |
| ------------------------ | ------------------------------------------------ | | ---------------- | ------------------------------------------ |
| 查函数库入口 | [catalog/index.md](catalog/index.md) | | 查函数库入口 | [catalog/index.md](catalog/index.md) |
| 直接按模块查函数 | [catalog/index.md](catalog/index.md) | | 直接按模块查函数 | [catalog/index.md](catalog/index.md) |
| 查可调用函数参数类型 | [verified/index.md](verified/index.md) |
| 查当前测试环境不支持方法 | [unavailable_methods.md](unavailable_methods.md) |
| 回到语法层 | [../syntax/index.md](../syntax/index.md) | | 回到语法层 | [../syntax/index.md](../syntax/index.md) |
| 回到金融层 | [../finance/index.md](../finance/index.md) | | 回到金融层 | [../finance/index.md](../finance/index.md) |
| 回到模块层 | [../modules/index.md](../modules/index.md) | | 回到模块层 | [../modules/index.md](../modules/index.md) |

View File

@ -1,11 +0,0 @@
# Reference Unavailable Methods
文档类型:当前测试环境不支持的方法清单
是否可直接用于生成代码:否
遇到不确定时跳转到:[verified/core.md](verified/core.md)、[catalog/index.md](catalog/index.md)
这里只记录方法不存在,或当前测试环境暂不支持的方法。参数类型事实写在 verified 函数页。
## 当前测试环境不支持的方法
暂无已入档记录。

View File

@ -1,49 +0,0 @@
# Verified Core Reference Functions
文档类型agent 参数事实表
是否可直接用于生成代码:是
遇到不确定时跳转到:[../catalog/index.md](../catalog/index.md)、[../../syntax/index.md](../../syntax/index.md)
本页只保留 agent 生成代码需要的函数事实:签名、接收类型、返回值和调用约束。
## 使用规则
- 只按“接收类型”传参。
- TSL 是弱类型语言,但函数参数不是任意类型。
- catalog 同名函数没有出现在 verified 页时,不要当作可调用事实。
## 函数参数
### `Abs(value)`
用途:返回数值绝对值。
| 参数 | 接收类型 | 返回 | 说明 |
| ------- | -------- | ---- | ---------- |
| `value` | 整数 | 数值 | 返回绝对值 |
| `value` | 实数 | 数值 | 返回绝对值 |
### `ifInt(value)`
用途:判断值当前是否按整数处理,返回 `1``0`
| 参数 | 接收类型 | 返回 | 说明 |
| ------- | --------------------------------------- | ---- | ---------------------- |
| `value` | 任意值;已记录整数、实数、字符串、`nil` | 整数 | 整数返回 `1`,否则 `0` |
### `DateToStr(value)`
用途:把日期时间值转成日期字符串。
| 参数 | 接收类型 | 返回 | 说明 |
| ------- | -------- | ------ | ------------------------------ |
| `value` | 日期时间 | 字符串 | 日期时间字面量可写 `20111231T` |
### `Length(value)`
用途:返回字符串或数组长度。
| 参数 | 接收类型 | 返回 | 说明 |
| ------- | -------- | ---- | ------------ |
| `value` | 字符串 | 整数 | 返回字符长度 |
| `value` | 数组 | 整数 | 返回元素个数 |

View File

@ -1,19 +0,0 @@
# Verified Reference Functions
文档类型agent 参数事实索引
是否可直接用于生成代码:否
遇到不确定时跳转到:[../catalog/index.md](../catalog/index.md)、[../../syntax/index.md](../../syntax/index.md)
本页只负责定位已经整理成参数事实表的函数页。生成调用代码时,进入具体函数页读取签名、接收类型、返回值和调用约束。
## 使用规则
- 只从具体函数页读取接收类型和返回值。
- catalog 里的候选名没有出现在具体函数页时,不要当作可调用事实。
- 不根据函数名猜参数。
## 参数事实页
| 范围 | 页面 | 当前函数 |
| -------- | ------------------ | ------------------------------------- |
| 核心函数 | [core.md](core.md) | `Abs`、`ifInt`、`DateToStr`、`Length` |

View File

@ -8,61 +8,47 @@
手册位置:第 1 篇,共 32 篇。上一篇:手册入口 [index.md](index.md)。下一篇:[02_quickstart.md](02_quickstart.md)。 手册位置:第 1 篇,共 32 篇。上一篇:手册入口 [index.md](index.md)。下一篇:[02_quickstart.md](02_quickstart.md)。
这一篇只做一件事:给 agent 建立 TSL 的第一判断模型。它不是人类教程,不负责展开所有语法细节;它只告诉 agent 第一次看到 TSL 任务时先判断什么、按什么证据生成代码、哪些直觉不能继承 这一篇只做一件事:让新 session 在看到 TSL 时,先用正确的读法建立心智模型
## 这一篇解决什么问题 ## 这一篇解决什么问题
回答“agent 第一次读到 TSL 需求时,怎样先识别交付形态,再选择最小可靠代码结构”。 回答“第一次读 TSL 时,应该先把它当成什么来理解,以及最容易和别的语言混淆的差异是什么”。
涉及赋值、命名参数、类外形、`unit` 骨架、数组 / 字符串下标这类高频硬规则时,统一以 [02_quickstart.md](02_quickstart.md) 的“语言核心事实速查”为准;这一页只负责建立第一判断模型 涉及赋值、命名参数、类外形、`unit` 骨架、数组 / 字符串下标这类高频硬规则时,统一以 [02_quickstart.md](02_quickstart.md) 的“语言核心事实速查”为准;这一页只负责建立第一印象
## Agent 第一判断流程 ## 先用什么方式读 TSL
1. 用户已给出 `.tsl` / `.tsf` 后缀时,后缀就是判断依据。 - 可以先按 Pascal 风格语言去读它:常见外形是 `function ... begin ... end;`、`unit ... interface ... implementation ... end.`、`type Name = class ... end;`;但这里只借外形,不默认继承 Pascal 的全部语义、库习惯和文件模型。
2. 用户未给后缀时,先看交付目标:可执行代码对应 `.tsl`,通用模块对应 `.tsf` - 先看顶层主体,再看细节;不要先被文件扩展名带偏。
3. 交付目标仍不明确时,向用户确认;不要替用户发明文件形态。 - 当前手册把顶层主体优先收敛成四类:松散语句、顶层 `function / procedure`、顶层 `type Name = class`、顶层 `unit`
4. 写 `.tsl` 时,先生成会顺序执行的语句区;如果需要函数或类,把声明区放在语句区之后。 - 顶层 `uses` 可以出现,但这里只把它当成辅助组织语句,不把它当成主体声明。
5. 写 `.tsf` 时,只生成模块 / 函数扩展代码;部署后的顶层函数可被 `.tsl` 脚本直接调用。
6. 没有文档证据时不要发明语法;只能回到对应专题页、反例页、项目规则,或记录文档缺口。
## 先用什么方式理解 TSL
- TSL 可以借 Pascal 风格外形理解:`function ... begin ... end;`、`unit ... interface ... implementation ... end.`、`type Name = class ... end;` 都很接近。
- 这里只借外形,不默认继承 Pascal 的全部语义、库习惯和文件模型。
- `.tsl` / `.tsf` 首先是用户交付形态:`.tsl` 是可执行脚本,`.tsf` 是通用模块 / 函数扩展。
- 日常赋值先记 `:=`,不要把 `=` 当成普通赋值。 - 日常赋值先记 `:=`,不要把 `=` 当成普通赋值。
## 第一次写时最容易混淆的差异
- `array(...)` 既可以当顺序数组,也可以当字符串键表。
- 数组下标从 `0` 开始,字符串下标从 `1` 开始。
- 顶层类定义按 `type Name = class ... end;` 去写,不使用裸 `class Name`
- 调用函数时,命名参数写成 `name: value`,例如 `Demo(a: 1, b: 2)`
## 已验证的第一印象 ## 已验证的第一印象
`.tsl` 可执行脚本第一印象: 最常见的 `function / procedure` 外形
代码块身份:已验证可执行示例 代码块身份:已验证可执行示例
```tsl ```tsl
a := 1; function Demo();
test();
function test();
begin begin
echo "test"; return 1;
end;
procedure LogDemo(msg);
begin
WriteLn(msg);
end; end;
``` ```
代码块说明:`.tsl` 的语句区在前,按顺序执行;函数声明区在后,供前面的脚本语句调用。
`.tsf` 通用模块第一印象:
代码块身份:已验证可执行示例
```tsl
function Test1();
begin
echo "test1";
end;
```
代码块说明:这个 `.tsf` 部署到解释器 `funcext` 后,`.tsl` 脚本可以直接调用 `Test1();`。文件部署方式属于项目执行层,不写进通用语法页。
赋值、数组、字符串下标的第一印象: 赋值、数组、字符串下标的第一印象:
代码块身份:已验证可执行示例 代码块身份:已验证可执行示例
@ -70,25 +56,43 @@ end;
```tsl ```tsl
items := array(10, 20, 30); items := array(10, 20, 30);
table_data := array("Code": "000001", "Name": "Demo"); table_data := array("Code": "000001", "Name": "Demo");
echo items[0]; first_item := items[0];
echo table_data["Code"]; first_char := "ABC"[1];
echo "ABC"[1];
``` ```
代码块说明:数组下标从 `0` 开始,字符串下标从 `1` 开始;更完整规则见 [04_values_and_literals.md](04_values_and_literals.md)。 类的第一印象:
## 第一次写时最容易混淆的边界 代码块身份:已验证可执行示例
- 不要把 `.tsl` / `.tsf` 当成 agent 自行选择的纯语法分支;后缀和用户交付目标优先。 ```tsl
- 不要把 Pascal 外形直接泛化成 Pascal 文件模型。 type DemoType = class
- 不要在 `.tsl` 的函数/类声明区之后继续追加脚本语句。 end;
- 不要把未验证写法写成语法事实agent 生成代码时只模仿带证据标签的代码块。 ```
`unit` 的第一印象:
代码块身份:已验证可执行示例
```tsl
unit DemoUnit;
interface
function Demo();
implementation
function Demo();
begin
return 1;
end;
end.
```
## 下一步怎么读 ## 下一步怎么读
- 需要立刻开始写代码:看 [02_quickstart.md](02_quickstart.md) - 需要立刻开始写代码:看 [02_quickstart.md](02_quickstart.md)
- 需要判断当前文件组织方式:看 [03_core_model.md](03_core_model.md) - 需要判断当前文件属于哪一种顶层模型:看 [03_core_model.md](03_core_model.md)
- 需要先写值、数组、字符串:看 [04_values_and_literals.md](04_values_and_literals.md) - 需要先写值、数组、字符串:看 [04_values_and_literals.md](04_values_and_literals.md)
- 需要先写变量、常量:看 [05_variables_and_constants.md](05_variables_and_constants.md) - 需要先写变量、常量:看 [05_variables_and_constants.md](05_variables_and_constants.md)
- 需要先写函数和调用:看 [06_functions_and_calls.md](06_functions_and_calls.md) - 需要先写函数和调用:看 [06_functions_and_calls.md](06_functions_and_calls.md)
- 需要先避开高频误写:看 [12_pitfalls.md](12_pitfalls.md)

View File

@ -8,31 +8,19 @@
手册位置:第 2 篇,共 32 篇。上一篇:[01_introduction.md](01_introduction.md)。下一篇:[03_core_model.md](03_core_model.md)。 手册位置:第 2 篇,共 32 篇。上一篇:[01_introduction.md](01_introduction.md)。下一篇:[03_core_model.md](03_core_model.md)。
这一篇集中回答两个紧邻问题:用户已给出 `.tsl` / `.tsf` 后缀时agent 应该如何按后缀组织代码;以及落代码前必须先核对哪些语言硬规则。 这一篇集中回答两个紧邻问题:现在要写 TSL 时,应该从哪一种最短骨架起手;以及落代码前必须先核对哪些语言硬规则。
## 这一篇解决什么问题 ## 这一篇解决什么问题
快速回答“当前任务已经给出后缀或交付目标时,应该使用哪一种骨架”,并提供一份单点的语言核心事实速查。 快速回答“当前任务应该从松散语句、函数、类还是 unit 开始”,并提供一份单点的语言核心事实速查。
## Agent 快速落代码流程
1. 先看用户有没有指定 `.tsl` / `.tsf` 后缀;指定后缀时,后缀就是文件形态判断依据。
2. 用户未指定后缀时,再根据交付目标判断 `.tsl``.tsf`:可执行脚本用 `.tsl`,通用模块 / 函数扩展用 `.tsf`
3. 只从 `代码块身份:已验证可执行示例` 的骨架起手;遇到 `反例 / 不可照写` 必须避开。
4. 写 `.tsl` 时,先写会执行的语句区;需要函数或类时,把声明区放在语句区之后。
5. 写 `.tsf` 时,只写模块 / 函数扩展内容;部署、查找路径和解释器环境属于项目执行层。
6. 当前页和对应专题页没有覆盖的写法,不要发明语法;改为跳转补证、向用户确认,或记录文档缺口。
## 语言核心事实速查 ## 语言核心事实速查
这一节是当前语法手册默认的语言硬规则收口点。涉及赋值、`.tsl` 语句区 / 声明区、`.tsf` 模块、命名参数、类写法、`unit` 骨架和下标规则时,统一先看这里。 这一节是当前语法手册默认的语言硬规则收口点。涉及赋值、顶层外形、命名参数、类写法、`unit` 骨架和下标规则时,统一先看这里。
- 普通赋值用 `:=`,不要把 `=` 当成普通赋值。 - 普通赋值用 `:=`,不要把 `=` 当成普通赋值。
- 用户已给出 `.tsl` / `.tsf` 后缀时,后缀就是判断依据;用户未给后缀时,再按交付目标判断。 - 顶层主体先按四类理解:松散语句、`function / procedure`、`type Name = class`、`unit`。
- 未给后缀时,可执行代码对应 `.tsl`,通用模块对应 `.tsf`;仍不明确时向用户确认。 - 如果当前文件采用顶层 `function / procedure` 模型,就不要再混入松散语句。
- `.tsl` 是可执行脚本:语句区在前并按顺序执行;函数/类声明区在后,供前面的语句调用或运行时解析。
- `.tsf` 是模块/函数扩展文件:部署到解释器 `funcext` 后,脚本可以直接调用其中暴露的顶层函数。
- 在 `.tsl` 中,如果需要函数或类,先写会执行的语句区,再写函数/类声明区;不要在声明区后面继续追加脚本语句。
- 无返回值时用 `procedure Name(...); begin ... end;`,不要勉强用 `function` - 无返回值时用 `procedure Name(...); begin ... end;`,不要勉强用 `function`
- 顶层类定义统一写成 `type Name = class ... end;`,不要写裸 `class Name` - 顶层类定义统一写成 `type Name = class ... end;`,不要写裸 `class Name`
- 多文件组织默认先按 `unit Name; interface ... implementation ... end.` 理解。 - 多文件组织默认先按 `unit Name; interface ... implementation ... end.` 理解。
@ -41,30 +29,26 @@
## 术语对照 ## 术语对照
- 文档里出现的“脚本语句区”,指 `.tsl` 文件开头会按顺序执行的语句。 - 文档里出现的“顶层 `function / procedure`”“顶层函数骨架”“顶层函数定义体”,指的是同一类顶层模型:文件以顶层 `function` / `procedure` 为主体。
- 文档里出现的“声明区”,指 `.tsl` 语句区之后的 `function / procedure``type Name = class` 声明。
- 文档里出现的“顶层 `function / procedure`”“顶层函数骨架”“顶层函数定义体”,在 `.tsf` 中指模块暴露的顶层函数,在 `.tsl` 中指脚本声明区里的函数。
- 文档里出现的 `class function` 和“类方法”,指的是同一件事:前者是代码关键字写法,后者是中文描述。 - 文档里出现的 `class function` 和“类方法”,指的是同一件事:前者是代码关键字写法,后者是中文描述。
## 先选哪一种骨架 ## 先选哪一种骨架
| 当前任务 | 起手骨架 | | 当前任务 | 起手骨架 |
| -------------------------------- | --------------------------------- | | ------------------------------ | ------------------- |
| 写一次性可执行逻辑 | `.tsl` 脚本语句区 | | 只写一段一次性脚本逻辑 | 顶层松散语句 |
| 脚本逻辑需要调用本文件内函数 | `.tsl` 语句区 + 后置函数声明区 | | 先沉淀一个可复用逻辑块 | 顶层 `function` |
| 脚本逻辑需要对象状态、字段、方法 | `.tsl` 语句区 + 后置类声明区 | | 需要对象状态、字段、方法 | `type Name = class` |
| 沉淀可复用函数并给脚本直接调用 | `.tsf` 顶层函数,部署到 `funcext` | | 需要把接口和实现组织进一个模块 | `unit` |
| 需要把接口和实现组织进一个模块 | `.tsf` `unit` |
默认建议: 默认建议:
- 如果你只是要让 agent 先写出一段最稳、最容易续写的可执行代码,优先从 `.tsl` 脚本语句区开始。 - 如果你只是要让新 session 先写出一段最稳、最容易续写的基础语法,优先从顶层 `function` 开始。
- 不要把 `unit` 当成最小起手骨架;只有用户明确要模块接口 / 实现组织,或项目已有 `unit` 边界时,才进入 `unit` 写法。
- `uses` 往往天然进入多文件查找路径问题,所以不放进这篇的最小起手骨架里。 - `uses` 往往天然进入多文件查找路径问题,所以不放进这篇的最小起手骨架里。
## 已验证最小骨架 ## 已验证最小骨架
`.tsl` 脚本语句区骨架: 顶层松散语句骨架:
代码块身份:已验证可执行示例 代码块身份:已验证可执行示例
@ -72,66 +56,27 @@
a := 1; a := 1;
``` ```
`.tsl` 语句区调用后置函数声明 顶层函数骨架
代码块身份:已验证可执行示例 代码块身份:已验证可执行示例
```tsl ```tsl
a := 1; function Demo();
test();
function test();
begin begin
echo "test"; return 1;
end; end;
``` ```
代码块身份:已验证输出片段 顶层类骨架:
```text
test
```
`.tsl` 语句区调用后置类声明:
代码块身份:已验证可执行示例 代码块身份:已验证可执行示例
```tsl ```tsl
obj := CreateObject("MyClass"); type DemoType = class
obj.value := 5;
echo obj.value;
type MyClass = class
value;
end; end;
``` ```
代码块身份:已验证输出片段 顶层 `unit` 骨架:
```text
5
```
`.tsf` 顶层函数骨架:
代码块身份:已验证可执行示例
```tsl
function Test1();
begin
echo "test1";
end;
```
代码块说明:这个 `.tsf` 部署到解释器 `funcext` 后,`.tsl` 脚本可以直接调用 `Test1();`。部署方式属于项目执行层,不写进通用语法页。
代码块身份:已验证输出片段
```text
test1
```
`.tsf` `unit` 骨架:
代码块身份:已验证可执行示例 代码块身份:已验证可执行示例
@ -148,50 +93,35 @@ implementation
end. end.
``` ```
代码块说明:这个 `.tsf` 骨架用于模块接口 / 实现组织;已通过 `.tsl` 脚本 `uses DemoUnit` 调用 `Ping()` 验证返回值为 `1`。调用脚本和查找路径边界见 [10_units_and_scope.md](10_units_and_scope.md)。
## 最常用起手版本 ## 最常用起手版本
如果你现在没有明确的模块复用或对象建模需求,直接从 `.tsl` 脚本版本开始: 如果你现在没有明确的多文件或对象建模需求,直接从函数版本开始:
代码块身份:已验证可执行示例 代码块身份:已验证可执行示例
```tsl ```tsl
echo "hello"; function Hello();
``` begin
return 1;
代码块身份:已验证输出片段 end;
```text
hello
``` ```
## 最容易写错的一件事 ## 最容易写错的一件事
- `.tsl` 可以同时有语句区和声明区;真正要避免的是在声明区后面继续追加脚本语句 - 不要把“顶层函数定义”和“顶层松散语句”混写在同一个最小文件里
代码块身份:反例 / 不可照写 代码块身份:反例 / 不可照写
```text ```text
a := 1; function Add(a, b);
test();
function test();
begin begin
echo "test"; return a + b;
end; end;
echo "after declaration"; value := Add(1, 2);
``` ```
上面最后一行属于“声明区之后继续写脚本语句”。写 `.tsl` 时先把会执行的语句放在前面,再把函数/类声明放在后面。 上面这种“先定义顶层函数,再接松散语句”的混合写法会编译失败。
代码块身份:已验证输出片段
```text
Execute script error at Line:9
function:__main__:line 9: invalid statement
```
## 跳转指引 ## 跳转指引

View File

@ -8,87 +8,26 @@
手册位置:第 3 篇,共 32 篇。上一篇:[02_quickstart.md](02_quickstart.md)。下一篇:[04_values_and_literals.md](04_values_and_literals.md)。 手册位置:第 3 篇,共 32 篇。上一篇:[02_quickstart.md](02_quickstart.md)。下一篇:[04_values_and_literals.md](04_values_and_literals.md)。
这一篇说明 TSL 的文件模型判断规则:agent 如何区分 `.tsl` 可执行脚本与 `.tsf` 模块,如何识别脚本语句区和声明区,以及为什么很多错误其实是“文件模型选错了”。 这一篇说明 TSL 的文件模型判断规则:顶层主体是什么、辅助语句是什么、为什么很多错误其实是“文件模型选错了”。
## 这一篇解决什么问题 ## 这一篇解决什么问题
回答“当前文件到底`.tsl` 脚本还是 `.tsf` 模块,以及 `.tsl` 里的哪些内容会顺序执行、哪些内容只是后置声明”。 回答“当前文件到底属于哪一种顶层写法,以及哪些语句只是辅助组织,不应该被误当成主体声明”。
如果问题已经变成赋值、命名参数、类外形、`unit` 骨架或下标规则这类通用硬规则,统一回 [02_quickstart.md](02_quickstart.md) 的“语言核心事实速查”;这一页只处理文件模型判断。 如果问题已经变成赋值、命名参数、类外形、`unit` 骨架或下标规则这类通用硬规则,统一回 [02_quickstart.md](02_quickstart.md) 的“语言核心事实速查”;这一页只处理文件模型判断。
## Agent 文件模型判断流程
1. 后缀是第一证据:用户明确要求 `.tsl` 时,按可执行脚本写;用户明确要求 `.tsf` 时,按模块 / 函数扩展写。
2. 没有后缀时看交付目标:可执行交付对应 `.tsl`,可复用扩展交付对应 `.tsf`
3. 目标仍不明确时先问用户;不要把脚本入口和通用模块替用户合并成一个猜测文件。
4. 写 `.tsl` 时,先生成脚本语句区;需要函数、过程或类时,把声明区放在语句区之后。
5. 写 `.tsf` 时,生成顶层函数 / 过程或 `unit`;不要写成会直接顺序执行的脚本入口。
6. 没有文档证据时不要发明文件模型;只能回到对应专题页、项目规则,或记录文档缺口。
## 必须记住的规则 ## 必须记住的规则
- 用户已给出 `.tsl` / `.tsf` 后缀时,后缀就是判断依据;未给后缀时,再按交付目标判断。 - 比起扩展名,更可靠的判断方式是看顶层内容。
- 未给后缀时,可执行代码对应 `.tsl`,通用模块对应 `.tsf`;仍不明确时向用户确认。 - 顶层主体在当前手册里优先按四种外形理解:顶层松散语句、`function / procedure`、`type Name = class`、`unit`。
- `.tsl` 脚本按两段理解:语句区在前并按顺序执行;声明区在后,可放 `function / procedure``type Name = class`
- `.tsf` 模块按可部署扩展理解:顶层函数部署到解释器 `funcext` 后,`.tsl` 可以直接调用;`unit` 仍按模块组织理解。
- `uses` 可以出现在顶层,但这里只把它当成辅助语句,不把它当成主体声明;函数体和类定义体里的位置限制见 [10_units_and_scope.md](10_units_and_scope.md)。 - `uses` 可以出现在顶层,但这里只把它当成辅助语句,不把它当成主体声明;函数体和类定义体里的位置限制见 [10_units_and_scope.md](10_units_and_scope.md)。
- 裸 `class Name` 不作为类定义写法使用。 - 裸 `class Name` 不作为类定义写法使用。
- `.tsl` 中,不要在声明区之后继续追加脚本语句。 - 如果一个文件已经采用“顶层 `function / procedure`”模型,就不要再混入松散语句。
- `unit` 默认先按完整形态理解;它也可以省略 `interface` / `implementation` 写成简写形态,见 [10_units_and_scope.md](10_units_and_scope.md)。 - `unit` 默认先按完整形态理解;它也可以省略 `interface` / `implementation` 写成简写形态,见 [10_units_and_scope.md](10_units_and_scope.md)。
- 不要把 `.tsl` 写成只有顶层函数的模块;如果用户要通用可复用函数,优先写 `.tsf`
- 不要把 `.tsf` 写成会直接执行脚本语句的入口;如果用户要顺序执行入口,优先写 `.tsl`
## 已验证语法 ## 已验证语法
`.tsl` 脚本语句区的最小形态: 顶层主体的最小形态:
代码块身份:已验证可执行示例
```tsl
a := 1;
```
`.tsl` 语句区后接函数声明区:
代码块身份:已验证可执行示例
```tsl
a := 1;
test();
function test();
begin
echo "test";
end;
```
代码块身份:已验证输出片段
```text
test
```
`.tsl` 语句区后接类声明区:
代码块身份:已验证可执行示例
```tsl
obj := CreateObject("MyClass");
obj.value := 5;
echo obj.value;
type MyClass = class
value;
end;
```
代码块身份:已验证输出片段
```text
5
```
`.tsf` 顶层函数的最小形态:
代码块身份:已验证可执行示例 代码块身份:已验证可执行示例
@ -99,39 +38,35 @@ begin
end; end;
``` ```
代码块说明:这个 `.tsf` 部署为函数扩展后,可由 `.tsl` 脚本调用 `Demo()` 并取得返回值。 代码块身份:已验证可执行示例
代码块身份:已验证输出片段 ```tsl
procedure DemoProc();
```text begin
1 end;
``` ```
`.tsf` `unit` 的最小形态: 代码块身份:已验证可执行示例
```tsl
type DemoType = class
end;
```
代码块身份:已验证可执行示例 代码块身份:已验证可执行示例
```tsl ```tsl
unit DemoUnit; unit DemoUnit;
interface interface
function Ping(); function Ping();
implementation implementation
function Ping(); function Ping();
begin begin
return 1; return 1;
end; end;
end. end.
``` ```
代码块说明:这个 `.tsf` `unit` 已通过 `.tsl` 脚本 `uses DemoUnit` 调用 `Ping()` 验证返回值为 `1`;调用脚本和查找路径边界见 [10_units_and_scope.md](10_units_and_scope.md)。
代码块身份:已验证输出片段
```text
1
```
已验证失败的形态: 已验证失败的形态:
代码块身份:反例 / 不可照写 代码块身份:反例 / 不可照写
@ -143,20 +78,14 @@ end;
上面这种裸 `class` 顶层写法会编译失败。 上面这种裸 `class` 顶层写法会编译失败。
代码块身份:已验证输出片段
```text
invalid statement
```
## 最小可编译示例 ## 最小可编译示例
如果当前任务只是“先让 agent 判断当前文件属于哪一种模型”,先记住下面这组归类: 如果当前任务只是“先让 session 判断当前文件属于哪一种模型”,先记住下面这组归类:
- `.tsl` 脚本语句区:直接写会顺序执行的语句。 - 顶层语句骨架:直接写松散语句。
- `.tsl` 声明区:在语句区之后写 `function ... begin ... end;`、`procedure ... begin ... end;` 或 `type Name = class ... end;`。 - 顶层函数 / 过程骨架:写 `function ... begin ... end;``procedure ... begin ... end;`
- `.tsf` 顶层函数 / 过程:写可部署到 `funcext``function` / `procedure` 文件 - 顶层类骨架:写 `type Name = class ... end;`
- `.tsf` 顶层单元:默认先写 `unit ... interface ... implementation ... end.`;简写形态见 [10_units_and_scope.md](10_units_and_scope.md)。 - 顶层单元骨架:默认先写 `unit ... interface ... implementation ... end.`;简写形态见 [10_units_and_scope.md](10_units_and_scope.md)。
代码块身份:已验证可执行示例 代码块身份:已验证可执行示例
@ -166,10 +95,9 @@ a := 1;
## 常见误写 ## 常见误写
- 把 `.tsl` 当成 `.tsf` 来写,只给一个顶层函数,不写任何会执行的脚本语句。 - 误以为扩展名本身就完全决定能否写函数、类或 `unit`
- 把 `.tsf` 当成 `.tsl` 来写,在模块文件里直接堆顺序执行的脚本语句。
- 把 `uses` 当成主体声明,而不是辅助组织语句。 - 把 `uses` 当成主体声明,而不是辅助组织语句。
- `.tsl` 声明区之后继续追加脚本语句 - `function / procedure` 定义体和松散语句混写
## 跳转指引 ## 跳转指引

View File

@ -14,18 +14,9 @@
回答“基本类型怎么写、数组和字符串怎么索引、哪些值规则属于语言级事实”。 回答“基本类型怎么写、数组和字符串怎么索引、哪些值规则属于语言级事实”。
## Agent 值写法判断流程
1. 普通值优先从整数、实数、普通字符串、布尔和 `array(...)` 起手。
2. 字符串默认用普通字符串;`L""` / `U""` 只在编码或宽串场景,并且必须按本页已验证边界写。
3. 看到 `array(...)` 时,先判断要顺序数组还是字符串键表:顺序数组用位置元素,字符串键表用 `"Key": value`
4. 写下标前先判断对象类别:顺序数组和二进制缓冲区下标从 `0` 开始,字符串下标从 `1` 开始。
5. 写字符串区间前先记住 `s[start:end]``end` 会被包含;不要按半开区间推断。
6. 没有已验证代码块时不要发明值写法;尤其不要从 JSON、JavaScript、Pascal 或 C 字符串规则直接迁移。
## 必须记住的规则 ## 必须记住的规则
- 最先掌握的几类值是:整数、实数、普通字符串、布尔和 `array(...)`。 - 最先掌握的几类值是:整数、实数、字符串、布尔和 `array`
- 普通字符串既可以用双引号,也可以用单引号。 - 普通字符串既可以用双引号,也可以用单引号。
- 同类引号本身可以通过连续写两个同类引号放进字符串里。 - 同类引号本身可以通过连续写两个同类引号放进字符串里。
- `\\`、`\"`、`\n` 这类基础转义已验证可用。 - `\\`、`\"`、`\n` 这类基础转义已验证可用。
@ -64,26 +55,13 @@ name := "ABC";
flag1 := true; flag1 := true;
flag2 := false; flag2 := false;
items := array(1, 2, 3); items := array(1, 2, 3);
WriteLn(count);
WriteLn(price);
WriteLn(name);
WriteLn(flag1);
WriteLn(flag2);
WriteLn(items[1]);
``` ```
代码块身份:已验证输出片段 其中已经验证:
```text - `true` 可以直接写成布尔值。
1 - `false` 可以直接写成布尔值。
12.5 - 运行输出里,`true` / `false` 分别表现为 `1` / `0`
ABC
1
0
2
```
代码块说明:`true` / `false` 可以直接写成布尔值;运行输出里分别表现为 `1` / `0`
普通字符串字面量: 普通字符串字面量:
@ -426,29 +404,25 @@ WriteLn("A"#13#10"B");
代码块身份:已验证可执行示例 代码块身份:已验证可执行示例
```tsl ```tsl
items := array(10, 20, 30); arr := array(10, 20, 30);
row := array("Code": "0001", "Price": 12.3); hash := array("Code": "0001", "Price": 12.3);
s := "ABC"; s := "ABC";
WriteLn(items[0]); WriteLn("ARR0=", arr[0]);
WriteLn(items[1]); WriteLn("ARR1=", arr[1]);
WriteLn(row["Code"]); WriteLn("HASH=", hash["Code"]);
WriteLn(s[1]); WriteLn("STR1=", s[1]);
WriteLn(s[2]); WriteLn("STR2=", s[2]);
WriteLn(s[3]); WriteLn("STR3=", s[3]);
``` ```
代码块身份:已验证输出片段 已验证运行结果对应关系:
```text - `arr[0] = 10`
10 - `arr[1] = 20`
20 - `hash["Code"] = "0001"`
0001 - `s[1] = "A"`
A - `s[2] = "B"`
B - `s[3] = "C"`
C
```
代码块说明:顺序数组 `items``0` 开始,字符串键表 `row` 用字符串键访问,字符串 `s``1` 开始。
二进制缓冲区: 二进制缓冲区:
@ -482,13 +456,10 @@ s := "ABCDE";
WriteLn(s[2:4]); WriteLn(s[2:4]);
``` ```
代码块身份:已验证输出片段 已验证运行结果:
```text - 输出 `BCD`
BCD - 这说明字符串区间 `2:4` 会包含结束位 `4`
```
代码块说明:字符串区间 `2:4` 会包含结束位 `4`
字符串替换子串: 字符串替换子串:
@ -569,12 +540,6 @@ WriteLn(s[0]);
上面这类写法在运行时会报字符串下标越界。 上面这类写法在运行时会报字符串下标越界。
代码块身份:已验证输出片段
```text
String index out of bounds
```
代码块身份:反例 / 不可照写 代码块身份:反例 / 不可照写
```text ```text

View File

@ -14,15 +14,6 @@
回答“普通变量怎样直接使用、`var` 在什么位置出现、常量有哪些基本写法、哪些名字一旦绑定就不能再赋值”。 回答“普通变量怎样直接使用、`var` 在什么位置出现、常量有哪些基本写法、哪些名字一旦绑定就不能再赋值”。
## Agent 变量/常量判断流程
1. 普通变量默认直接用 `:=` 首次赋值,不要先补一个没有需求证据的 `var` 段。
2. 只有用户要求显式声明或遇到 `{$Explicit+}` 时才优先写 `var`
3. 顶层脚本常量优先用 `const name := value;`;需要后续脚本语句时,仍按 `.tsl` 语句区规则组织。
4. `const Name = value;` 优先放在函数 `const` 段、`unit` 接口或类成员里;不要和顶层 `const :=` 任意互换。
5. 多参数赋值按 `[a, b] := array(...)` 写;单变量拆包必须写成 `[name, ] := array(...)`
6. 没有已验证代码块时不要发明变量/常量写法;尤其不要从 Pascal 的声明习惯反推 TSL 必须先声明变量。
## 必须记住的规则 ## 必须记住的规则
- 默认变量模型是“直接赋值即得到变量”,不要求先写 `var` - 默认变量模型是“直接赋值即得到变量”,不要求先写 `var`
@ -50,15 +41,6 @@
```tsl ```tsl
a := 1; a := 1;
b := array(1, 2, 3); b := array(1, 2, 3);
WriteLn(a);
WriteLn(b[1]);
```
代码块身份:已验证输出片段
```text
1
2
``` ```
显式 `var` 写法: 显式 `var` 写法:
@ -84,12 +66,6 @@ begin
end. end.
``` ```
代码块身份:已验证输出片段
```text
1
```
顶层最稳的常量写法: 顶层最稳的常量写法:
代码块身份:已验证可执行示例 代码块身份:已验证可执行示例
@ -107,12 +83,6 @@ const value := 1 + 2 * 3;
WriteLn(value); WriteLn(value);
``` ```
代码块身份:已验证输出片段
```text
7
```
函数内部 `const` 段: 函数内部 `const` 段:
代码块身份:已验证可执行示例 代码块身份:已验证可执行示例
@ -178,12 +148,9 @@ WriteLn(r1);
WriteLn(r2); WriteLn(r2);
``` ```
代码块身份:已验证输出片段 已验证运行结果:
```text - 依次输出 `1`、`3`
1
3
```
单变量拆包时,末尾逗号不能省略: 单变量拆包时,末尾逗号不能省略:
@ -194,11 +161,9 @@ WriteLn(r2);
WriteLn(re); WriteLn(re);
``` ```
代码块身份:已验证输出片段 已验证运行结果:
```text - 输出 `1`
1
```
左侧变量比右侧数组更长时,多出的变量为 `nil` 左侧变量比右侧数组更长时,多出的变量为 `nil`
@ -210,12 +175,10 @@ WriteLn(r1);
WriteLn(r2 = nil); WriteLn(r2 = nil);
``` ```
代码块身份:已验证输出片段 已验证运行结果:
```text - `r1` 输出 `1`
1 - `r2 = nil` 输出 `1`
1
```
右侧元素也可以是数组: 右侧元素也可以是数组:
@ -330,12 +293,6 @@ end.
上面这类写法在当前解释器里会编译失败,主因是 `variable not defined` 上面这类写法在当前解释器里会编译失败,主因是 `variable not defined`
代码块身份:已验证输出片段
```text
variable not defined
```
## 跳转指引 ## 跳转指引
- 回看基本类型:见 [04_values_and_literals.md](04_values_and_literals.md) - 回看基本类型:见 [04_values_and_literals.md](04_values_and_literals.md)

View File

@ -12,24 +12,13 @@
## 这一篇解决什么问题 ## 这一篇解决什么问题
回答“如何正确声明 `function``procedure`、`.tsl` 脚本语句区如何调用后置函数声明、怎样使用参数修饰、默认参数与可变参数,以及哪些函数写法会直接编译失败”。 回答“如何正确声明 `function``procedure`、如何组织主函数和子函数、怎样使用参数修饰、默认参数与可变参数,以及哪些混写方式会直接编译失败”。
## Agent 函数/调用判断流程
1. 先判断当前文件是 `.tsl` 还是 `.tsf`;文件形态不明确时回 [03_core_model.md](03_core_model.md)。
2. `.tsl` 中先写语句区,再把 `function` / `procedure` 声明放在后面;不要在声明区后面追加脚本语句。
3. `.tsf` 中把顶层 `function` / `procedure` 当成模块 / 函数扩展声明;不要写成顺序执行入口。
4. 需要返回值时用 `function`,不需要返回值时用 `procedure`
5. 调用普通 TSL 函数时,命名参数只写 `name: value`;不要把 `name = value` 当成命名参数。
6. 参数是否写回调用方要看 `const` / `var` / `{$VarByRef-}` / `in` / `out`,不要默认按其他语言习惯推断。
7. 没有已验证代码块时不要发明函数/调用写法;尤其不要把二进制函数、系统函数、函数指针和匿名函数都套成同一种调用语法。
## 必须记住的规则 ## 必须记住的规则
- 最稳妥的函数骨架仍然是 `function Name(...); begin ... end;` - 最稳妥的函数骨架仍然是 `function Name(...); begin ... end;`
- 不需要返回值时,可以改用 `procedure Name(...); begin ... end;` - 不需要返回值时,可以改用 `procedure Name(...); begin ... end;`
- 在 `.tsl` 文件模型层,脚本语句后可以接函数声明;语句区在前顺序执行,声明区在后提供函数/过程定义。见 [03_core_model.md](03_core_model.md)。 - 在文件模型层,`function` 和 `procedure` 归同一类顶层外形;见 [03_core_model.md](03_core_model.md)。
- 在 `.tsf` 文件模型层,顶层 `function` / `procedure` 是模块/函数扩展声明;部署到解释器 `funcext` 后可被脚本直接调用。
- 当前解释器接受省略函数头后的分号,但文档默认仍保留这个分号。 - 当前解释器接受省略函数头后的分号,但文档默认仍保留这个分号。
- 一个函数定义体里可以同时出现主函数和子函数。 - 一个函数定义体里可以同时出现主函数和子函数。
- 函数支持参数类型注解和返回值类型注解。 - 函数支持参数类型注解和返回值类型注解。
@ -59,32 +48,12 @@
- 当前解释器没有通过 `f(...)` 这种“函数变量直接调用”写法;无论 `f` 是匿名函数、`FindFunction(...)` 还是 `ThisFunction(...)` 返回的函数指针,都不要默认写成直调。 - 当前解释器没有通过 `f(...)` 这种“函数变量直接调用”写法;无论 `f` 是匿名函数、`FindFunction(...)` 还是 `ThisFunction(...)` 返回的函数指针,都不要默认写成直调。
- 当前解释器接受 `::FuncName(...)` 指向全局/系统函数,用来绕过当前作用域里的同名局部函数。 - 当前解释器接受 `::FuncName(...)` 指向全局/系统函数,用来绕过当前作用域里的同名局部函数。
- `external`、`MakeInstance` 和线程调用统一移到 [21_external_calls_and_threads.md](21_external_calls_and_threads.md)。 - `external`、`MakeInstance` 和线程调用统一移到 [21_external_calls_and_threads.md](21_external_calls_and_threads.md)。
- 不要`.tsl` 的函数声明区之后继续追加脚本语句 - 不要把顶层函数定义和松散语句混在同一个文件模型里
## 已验证语法 ## 已验证语法
### 基础函数 / 过程骨架 ### 基础函数 / 过程骨架
`.tsl` 语句区调用后置函数声明:
代码块身份:已验证可执行示例
```tsl
a := 1;
test();
function test();
begin
echo "test";
end;
```
代码块身份:已验证输出片段
```text
test
```
最短函数骨架: 最短函数骨架:
代码块身份:已验证可执行示例 代码块身份:已验证可执行示例
@ -152,11 +121,9 @@ begin
end. end.
``` ```
代码块身份:已验证输出片段 已验证运行结果:
```text - `Bump(a)` 后输出 `2`
2
```
### 签名增强 ### 签名增强
@ -349,7 +316,10 @@ end.
代码块身份:已验证可执行示例 代码块身份:已验证可执行示例
```tsl ```tsl
WriteLn(Pack(a: 1, b: 2)); function NamedArgsDemo();
begin
return Pack(a: 1, b: 2);
end;
function Pack(a, b); function Pack(a, b);
begin begin
@ -357,13 +327,7 @@ begin
end; end;
``` ```
代码块身份:已验证输出片段 已验证运行结果:
```text
12
```
已验证补充:
- `Pack(a: 1, b: 2)` 返回 `12` - `Pack(a: 1, b: 2)` 返回 `12`
- `Pack(b: 2, a: 1)` 返回 `12` - `Pack(b: 2, a: 1)` 返回 `12`
@ -856,25 +820,12 @@ end.
## 最小可编译示例 ## 最小可编译示例
如果你只是要写一个能被 agent 稳定续写的 `.tsl` 脚本,从语句区起步,需要函数时把声明区放在后面 如果你只是要写一个能被 session 稳定续写的函数 / 过程,从下面任一骨架起步
代码块身份:已验证可执行示例 代码块身份:已验证可执行示例
```tsl ```tsl
Hello();
function Hello(); function Hello();
begin
echo "hello";
end;
```
如果你要写 `.tsf` 模块/函数扩展,从下面任一骨架起步:
代码块身份:已验证可执行示例
```tsl
function HelloValue();
begin begin
return 1; return 1;
end; end;
@ -890,7 +841,7 @@ end;
## 常见误写 ## 常见误写
- `.tsl` 的声明区后面继续写脚本语句 - 把顶层函数定义和松散语句混写
- 以为函数头后的分号是当前解释器的硬性要求。 - 以为函数头后的分号是当前解释器的硬性要求。
- 以为 `procedure` 只是 `function` 的别名,不涉及参数传递语义。 - 以为 `procedure` 只是 `function` 的别名,不涉及参数传递语义。
- 带类型注解时仍然用逗号分隔参数。 - 带类型注解时仍然用逗号分隔参数。
@ -906,24 +857,15 @@ end;
代码块身份:反例 / 不可照写 代码块身份:反例 / 不可照写
```text ```text
a := 1;
Add(1, 2);
function Add(a, b); function Add(a, b);
begin begin
return a + b; return a + b;
end; end;
echo "after function"; value := Add(1, 2);
``` ```
上面的问题不在 `Add` 本身,而在于 `.tsl` 的函数声明区后面又继续出现脚本语句。正确做法是把会执行的语句全部放在声明区之前。 上面这种混写方式会编译失败,问题不在 `Add` 本身,而在于文件模型混了“函数定义体”和“松散语句”两种写法。
代码块身份:已验证输出片段
```text
invalid statement
```
代码块身份:反例 / 不可照写 代码块身份:反例 / 不可照写
@ -942,7 +884,7 @@ end;
Pack(a = 1, b = 2) Pack(a = 1, b = 2)
``` ```
这类写法不要当成命名参数。它虽然可能编译通过,但当前已验证返回结果不对,不能当成可靠的命名参数语法。 这类写法不要当成命名参数。它虽然可能编译通过,但在我于 `2026-04-09` 的实测里返回结果不对,不能当成可靠的命名参数语法。
代码块身份:反例 / 不可照写 代码块身份:反例 / 不可照写

View File

@ -8,42 +8,31 @@
手册位置:第 7 篇,共 32 篇。上一篇:[06_functions_and_calls.md](06_functions_and_calls.md)。下一篇:[08_control_flow.md](08_control_flow.md)。 手册位置:第 7 篇,共 32 篇。上一篇:[06_functions_and_calls.md](06_functions_and_calls.md)。下一篇:[08_control_flow.md](08_control_flow.md)。
这一篇集中放语言级表达式与运算符。agent 写代码时,只能从本页已验证代码块归纳表达式写法,不要把其他语言或外部资料里的运算符习惯直接搬进 TSL 这一篇集中放语言级表达式与运算符,避免与业务计算示例混用
## 这一篇解决什么问题 ## 这一篇解决什么问题
回答“赋值、比较、条件求值、表达式对象、空安全访问和链式比较在 TSL 里怎样写”。 回答“赋值、比较、条件求值、可空访问和一部分新表达式在 TSL 里怎样写”。
## Agent 表达式/运算符判断流程
1. 先判断要写赋值、比较、条件求值、表达式对象、空安全访问还是链式比较。
2. 普通赋值只能用 `:=`,不要把 `=` 当赋值写法。
3. 比较才用 `=`,并且把比较表达式放在 `WriteLn(...)`、条件或其他需要布尔值的位置。
4. 已有变量做复合运算时,才使用 `+=`、`-=`、`*=`、`/=`、`%=`、`a++;`、`a--;`。
5. 条件求值优先用 `flag ? true_value : false_value`;需要 Pascal 风格时可用 `if condition then true_value else false_value` 的形态,但必须带 `else`
6. 需要延迟求值或动态表达式对象时,才使用 `@expr``&"..."`,并用已验证的 `eval(...)` 形态求值。
7. 空安全访问只照本页已验证形态写:`a?.member`、`a?.[index]`、以及已验证的 `c?.a?.[1]`。不要外推成任意深度、任意组合都可写。
8. 需要连续比较时,标量用 `:<` / `:>` 这组链式比较;数组逐元素比较用 `::<` / `::>` 这组矩阵链式比较。
9. 没有已验证代码块时不要发明表达式/运算符写法。
## 必须记住的规则 ## 必须记住的规则
- 当前页只收已经单独验证过的基础表达式,不把未经逐条验证的扩展运算体系一次并进正文。 - 当前页只收已经单独验证过的基础表达式,不把未经逐条验证的扩展运算体系一次并进正文。
- 普通赋值使用 `:=` - 赋值使用 `:=`
- 已验证支持 `+=`、`-=`、`*=`、`/=`、`%=` 这几种基础运算赋值。 - 当前解释器接受 `+=`、`-=`、`*=`、`/=`、`%=` 这几种基础运算赋值。
- 已验证支持语句级 `a++;``a--;` - 当前解释器接受语句级 `a++;``a--;`
- `=` 用于比较,不用于赋值。 - `=` 用于比较,不用于赋值。
- 已验证支持字符串 `+` 拼接、字符串比较和 `like` 正则匹配。 - 当前解释器接受字符串 `+` 拼接、字符串比较和 `like` 正则匹配。
- 已验证支持 `flag ? true_value : false_value``if condition then true_value else false_value` 这两种条件求值写法。 - 当前解释器同时接受 `flag ? true_value : false_value``if condition then true_value else false_value` 这两种条件求值写法。
- 已验证支持 `@expr` 把后面的内容声明成表达式对象。 - 当前解释器接受 `@expr` 把后面的内容声明成表达式对象。
- 已验证支持 `&"..."` 把字符串编译成表达式对象。 - 当前解释器接受 `&"..."` 把字符串编译成表达式对象。
- 已验证支持逗号表达式 `(exp1, exp2, ..., expN)`,并按从左到右顺序求值。 - 当前解释器接受逗号表达式 `(exp1, exp2, ..., expN)`,并按从左到右顺序求值。
- 已验证支持空安全访问 `a?.member`、`a?.[index]` 和本页示例里的 `c?.a?.[1]` - 当前解释器接受空安全访问 `a?.member``a?.[index]`
- 已验证支持 `not in`、`not like`、`not sqlin`、`not is` 这几种否定形式运算。 - 当前解释器接受 `not in`、`not like`、`not sqlin`、`not is` 这几种否定形式运算。
- 已验证支持标量链式比较 `:>`、`:<`、`:<>`、`:==`、`:>=`、`:<=`。 - 当前解释器接受标量链式比较 `:>`、`:<`、`:<>`、`:==`、`:>=`、`:<=`。
- 已验证支持矩阵链式比较 `::>`、`::<`、`::<>`、`::==`、`::>=`、`::<=`。 - 当前解释器接受矩阵链式比较 `::>`、`::<`、`::<>`、`::==`、`::>=`、`::<=`。
- `{$IFDEF ifexp}` 可用于探测 `if ... then ... else ...` 表达式能力。 - 当前环境里 `{$IFDEF ifexp}` 为真,可用于探测 `if ... then ... else ...` 表达式能力。
- `{$IFDEF nilinvoke}` 可用于探测 nil 调用相关能力。 - 当前环境里 `{$IFDEF nilinvoke}` 为真,可用于探测 nil 调用相关能力。
- 不要把外部资料里的更深链式可空访问修正,直接当成当前解释器必然支持。
## 已验证语法 ## 已验证语法
@ -56,13 +45,6 @@ a := 1;
b := 2; b := 2;
flag := a < b; flag := a < b;
value := flag ? 10 : 20; value := flag ? 10 : 20;
WriteLn(value);
```
代码块身份:已验证输出片段
```text
10
``` ```
运算赋值: 运算赋值:
@ -75,36 +57,35 @@ a += 2;
WriteLn(a); WriteLn(a);
``` ```
代码块身份:已验证输出片段 已验证运行结果:
```text - 输出 `3`
3
```
基础算术复合赋值: 基础算术复合赋值:
代码块身份:已验证可执行示例 代码块身份:已验证可执行示例
```tsl ```tsl
a := 10; program test;
a -= 3; begin
WriteLn(a); a := 10;
a *= 4; a -= 3;
WriteLn(a); WriteLn(a);
a /= 7; a *= 4;
WriteLn(a); WriteLn(a);
a %= 5; a /= 7;
WriteLn(a); WriteLn(a);
a %= 5;
WriteLn(a);
end.
``` ```
代码块身份:已验证输出片段 已验证运行结果:
```text - `a -= 3` 后输出 `7`
7 - `a *= 4` 后输出 `28`
28 - `a /= 7` 后输出 `4`
4 - `a %= 5` 后输出 `4`
4
```
字符串同样支持 `+=` 字符串同样支持 `+=`
@ -116,57 +97,49 @@ s += "B";
WriteLn(s); WriteLn(s);
``` ```
代码块身份:已验证输出片段 已验证运行结果:
```text - 输出 `AB`
AB
```
字符串拼接、比较和 `like` 字符串拼接、比较和 `like`
代码块身份:已验证可执行示例 代码块身份:已验证可执行示例
```tsl ```tsl
WriteLn("222" + "888"); program test;
WriteLn("A" < "a"); begin
WriteLn("AB" < "ABC"); WriteLn("222" + "888");
WriteLn("ABC" = "ABC"); WriteLn("A" < "a");
WriteLn("2009-01-01" like "\\d{4}-\\d{2}-\\d{2}"); WriteLn("AB" < "ABC");
WriteLn("ABC" = "ABC");
WriteLn("2009-01-01" like "\\d{4}-\\d{2}-\\d{2}");
end.
``` ```
代码块身份:已验证输出片段 已验证运行结果:
```text - 依次输出 `222888`、`1`、`1`、`1`、`1`
222888 - 说明字符串可以直接用 `+` 拼接
1 - 说明字符串比较区分字符序和大小写;当前例子里 `"A" < "a"` 为真
1 - 说明当前 `like` 的右侧可以直接写正则模式
1
1
```
说明:
- 字符串可以直接用 `+` 拼接。
- 字符串比较区分字符序和大小写;当前例子里 `"A" < "a"` 为真。
- `like` 的右侧可以直接写正则模式。
`like` 不要按 SQL `%` 通配去理解: `like` 不要按 SQL `%` 通配去理解:
代码块身份:已验证可执行示例 代码块身份:已验证可执行示例
```tsl ```tsl
WriteLn("abc" like "a.*"); program test;
WriteLn("abc" like "a%"); begin
WriteLn("abc" like "a.*");
WriteLn("abc" like "a%");
end.
``` ```
代码块身份:已验证输出片段 已验证运行结果:
```text - 第一行输出 `1`
1 - 第二行输出 `0`
0 - 因此当前解释器里的 `like` 更接近“正则匹配”,不是 SQL 那套 `%` / `_` 通配语义
```
因此 `like` 更接近“正则匹配”,不是 SQL 那套 `%` / `_` 通配语义。
自增与自减: 自增与自减:
@ -180,30 +153,25 @@ a--;
WriteLn(a); WriteLn(a);
``` ```
代码块身份:已验证输出片段 已验证运行结果:
```text - 先输出 `2`
2 - 再输出 `1`
1
```
`if` 表达式: `if` 表达式:
代码块身份:已验证可执行示例 代码块身份:已验证可执行示例
```tsl ```tsl
WriteLn(if 2 > 1 then 2 else 1); program test;
begin
WriteLn(if 2 > 1 then 2 else 1);
end.
``` ```
代码块身份:已验证输出片段 已验证运行结果:
```text - `if 2 > 1 then 2 else 1` 返回 `2`
2
```
`if condition then true_value else false_value` 必须带 `else`,否则不是本页可照写的表达式形态。
### 表达式对象
`@` 表达式前导: `@` 表达式前导:
@ -216,13 +184,10 @@ C := eval(B);
WriteLn(C); WriteLn(C);
``` ```
代码块身份:已验证输出片段 已验证运行结果:
```text - 输出 `2`
2 - 说明 `@A + 1` 会得到一个可交给 `eval(...)` 求值的表达式对象
```
`@A + 1` 会得到一个可交给 `eval(...)` 求值的表达式对象。
`&"..."` 表达式常量: `&"..."` 表达式常量:
@ -235,13 +200,10 @@ C := eval(B);
WriteLn(C); WriteLn(C);
``` ```
代码块身份:已验证输出片段 已验证运行结果:
```text - 输出 `2`
2 - 说明 `&"A + 1"` 会把字符串编译成表达式对象,再由 `eval(...)` 求值
```
`&"A + 1"` 会把字符串编译成表达式对象,再由 `eval(...)` 求值。
逗号表达式: 逗号表达式:
@ -258,13 +220,10 @@ begin
end. end.
``` ```
代码块身份:已验证输出片段 已验证运行结果:
```text - 输出 `6`
6 - 说明逗号表达式会按从左到右顺序执行前面的赋值,再返回最后一个表达式结果
```
逗号表达式会按从左到右顺序执行前面的赋值,再返回最后一个表达式结果。
逗号表达式也可以继续参与外层计算: 逗号表达式也可以继续参与外层计算:
@ -275,13 +234,10 @@ A := (b := 2, c := 3, b * c) * c;
WriteLn(A); WriteLn(A);
``` ```
代码块身份:已验证输出片段 已验证运行结果:
```text - 输出 `18`
18 - 说明逗号表达式本身可以作为一个普通子表达式继续参与后续运算
```
逗号表达式本身可以作为一个普通子表达式继续参与后续运算。
### 空安全访问 ### 空安全访问
@ -303,30 +259,11 @@ begin
end. end.
``` ```
代码块身份:已验证输出片段 已验证运行结果:
```text - `a?.value = nil` 输出 `1`
1 - `h?.value` 输出 `7`
7 - `arr?.[0] = nil` 输出 `1`
1
```
更深一层的混合空安全访问,本页只确认下面这个形态:
代码块身份:已验证可执行示例
```tsl
c := nil;
WriteLn(c?.a?.[1] = nil);
```
代码块身份:已验证输出片段
```text
1
```
不要从这一段外推任意深度、任意成员/下标混合都可写。
### 否定形式运算 ### 否定形式运算
@ -347,92 +284,86 @@ begin
end. end.
``` ```
代码块身份:已验证输出片段 已验证运行结果:
```text - `1 not in array(2, 3)` 输出 `1`
1 - `"2009-1-1" not like "\\d{4}-\\d{2}-\\d{2}"` 输出 `1`
1 - `1 not sqlin array(2, 3)` 输出 `1`
1 - `obj not is class(B)` 输出 `1`
1
```
### 标量链式比较 ### 标量链式比较
代码块身份:已验证可执行示例 代码块身份:已验证可执行示例
```tsl ```tsl
WriteLn(1 :< 2 :< 3); program test;
WriteLn(3 :> 2 :> 1); begin
WriteLn(1 :== 1 :== 1); WriteLn(1 :< 2 :< 3);
WriteLn(3 :>= 2 :>= 2); WriteLn(3 :> 2 :> 1);
WriteLn(1 :<= 2 :<= 3); WriteLn(1 :== 1 :== 1);
WriteLn(1 :<> 2 :<> 3); WriteLn(3 :>= 2 :>= 2);
WriteLn(1 :<= 2 :<= 3);
WriteLn(1 :<> 2 :<> 3);
end.
``` ```
代码块身份:已验证输出片段 已验证运行结果:
```text - `1 :< 2 :< 3` 输出 `1`
1 - `3 :> 2 :> 1` 输出 `1`
1 - `1 :== 1 :== 1` 输出 `1`
1 - `3 :>= 2 :>= 2` 输出 `1`
1 - `1 :<= 2 :<= 3` 输出 `1`
1 - `1 :<> 2 :<> 3` 输出 `1`
1
```
### 矩阵链式比较 ### 矩阵链式比较
代码块身份:已验证可执行示例 代码块身份:已验证可执行示例
```tsl ```tsl
r := array(1, 2, -1) ::< array(2, 1, 0) ::< array(3, 2, 1); program test;
WriteLn(r[0]); begin
WriteLn(r[1]); r := array(1, 2, -1) ::< array(2, 1, 0) ::< array(3, 2, 1);
WriteLn(r[2]); WriteLn(r[0]);
s := array(1, 2, -1) ::< 2 ::< array(3, 2, 1); WriteLn(r[1]);
WriteLn(s[0]); WriteLn(r[2]);
WriteLn(s[1]); s := array(1, 2, -1) ::< 2 ::< array(3, 2, 1);
WriteLn(s[2]); WriteLn(s[0]);
WriteLn(s[1]);
WriteLn(s[2]);
end.
``` ```
代码块身份:已验证输出片段 已验证运行结果:
```text - `array(1, 2, -1) ::< array(2, 1, 0) ::< array(3, 2, 1)` 的三个元素依次输出 `1`、`0`、`1`
1 - `array(1, 2, -1) ::< 2 ::< array(3, 2, 1)` 的三个元素依次输出 `1`、`0`、`0`
0 - 说明矩阵链式比较会按元素位置分别得到结果数组,并且可以和标量混用
1
1
0
0
```
矩阵链式比较会按元素位置分别得到结果数组,并且可以和标量混用。
### 条件编译探测 ### 条件编译探测
代码块身份:已验证可执行示例 代码块身份:已验证可执行示例
```tsl ```tsl
program test;
begin
{$IFDEF ifexp} {$IFDEF ifexp}
WriteLn(1); WriteLn(1);
{$ELSE} {$ELSE}
WriteLn(0); WriteLn(0);
{$ENDIF} {$ENDIF}
{$IFDEF nilinvoke} {$IFDEF nilinvoke}
WriteLn(1); WriteLn(1);
{$ELSE} {$ELSE}
WriteLn(0); WriteLn(0);
{$ENDIF} {$ENDIF}
end.
``` ```
代码块身份:已验证输出片段 已验证运行结果:
```text - `{$IFDEF ifexp}` 输出 `1`
1 - `{$IFDEF nilinvoke}` 输出 `1`
1
```
这只能作为能力探测示例使用agent 不要把条件编译探测写成普通业务逻辑。
## 最小可编译示例 ## 最小可编译示例
@ -443,20 +374,13 @@ WriteLn(0);
```tsl ```tsl
flag := 1 < 2; flag := 1 < 2;
value := flag ? 10 : 20; value := flag ? 10 : 20;
WriteLn(value);
```
代码块身份:已验证输出片段
```text
10
``` ```
## 常见误写 ## 常见误写
- 用 `=` 当赋值运算符。 - 用 `=` 当赋值运算符。
- 把 `if` 表达式写成没有 `else` 的半句。 - 把 `if` 表达式写成没有 `else` 的半句。
- 把已验证的 `c?.a?.[1]` 外推成所有深链式空安全访问都可靠 - 把更深链式可空访问 `c?.a?.[1]` 直接当成当前解释器已支持事实。
代码块身份:反例 / 不可照写 代码块身份:反例 / 不可照写
@ -464,12 +388,6 @@ WriteLn(value);
a = 1; a = 1;
``` ```
代码块身份:已验证输出片段
```text
invalid statement
```
上面这种写法会编译失败,因为单独的 `=` 在这里会被当成不成立的表达式。 上面这种写法会编译失败,因为单独的 `=` 在这里会被当成不成立的表达式。
代码块身份:反例 / 不可照写 代码块身份:反例 / 不可照写
@ -478,13 +396,16 @@ invalid statement
v := if 2 > 1 then 2; v := if 2 > 1 then 2;
``` ```
代码块身份:已验证输出片段 上面这种写法也会编译失败;`if` 表达式当前必须带 `else`
代码块身份:反例 / 不可照写
```text ```text
invalid statement c := nil;
WriteLn(c?.a?.[1] = nil);
``` ```
上面这种写法也会编译失败;`if` 表达式必须带 `else` 上面这种更深的混合可空访问,在当前已记录验证里没有通过,不要提前把后续版本的修正结果写进结论
## 跳转指引 ## 跳转指引

View File

@ -14,14 +14,6 @@
回答“`if`、`case`、`for`、`while`、`repeat`、`break`、`continue`、`try`、`raise` 这些流程结构在 TSL 里到底怎么写,哪些写法已经被当前解释器实测验证过”。 回答“`if`、`case`、`for`、`while`、`repeat`、`break`、`continue`、`try`、`raise` 这些流程结构在 TSL 里到底怎么写,哪些写法已经被当前解释器实测验证过”。
## Agent 控制流判断流程
1. 先判断任务需要条件分支、循环、`case`、异常处理还是调试跳转。
2. `if` / `for` / `while` / `repeat` 优先照本页已验证骨架写,不要套用其他 Pascal 方言。
3. 需要多条语句时再补 `begin ... end`,不要在 `else` 前提前加分号。
4. `case` 表达式和 `case` 语句分开判断;`@case` 不作为默认主写法。
5. 没有已验证代码块时不要发明控制流写法。
## 必须记住的规则 ## 必须记住的规则
- `if ... then ... else ...` 可以直接跟单条语句;需要多条语句时再补 `begin ... end` - `if ... then ... else ...` 可以直接跟单条语句;需要多条语句时再补 `begin ... end`
@ -86,12 +78,6 @@ WriteLn(s);
- 输出 `9` - 输出 `9`
代码块身份:已验证输出片段
```text
9
```
`step``downto` 递减循环: `step``downto` 递减循环:
代码块身份:已验证可执行示例 代码块身份:已验证可执行示例
@ -379,7 +365,7 @@ end;
WriteLn(b); WriteLn(b);
``` ```
当前已验证结果显示,上面这类最小例子输出的是 `<STREXP>`,不能把它当成普通 `case` 表达式的可靠主语法写进新 session 默认生成结果。 我在 `2026-04-13` 用当前解释器实测,上面这类最小例子输出的是 `<STREXP>`,不能把它当成普通 `case` 表达式的可靠主语法写进新 session 默认生成结果。
## 跳转指引 ## 跳转指引

View File

@ -16,14 +16,6 @@
本页后半段有少量依赖 `unit` 的双文件示例;如果你还没建立 `unit` / `uses` 的多文件心智模型,先看 [10_units_and_scope.md](10_units_and_scope.md)。 本页后半段有少量依赖 `unit` 的双文件示例;如果你还没建立 `unit` / `uses` 的多文件心智模型,先看 [10_units_and_scope.md](10_units_and_scope.md)。
## Agent 对象/类判断流程
1. 先判断要写普通对象、继承、构造函数、类方法、静态字段还是运行时创建。
2. 对象声明优先使用本页已验证的 `type Name = class ... end;` 骨架。
3. 类方法和静态字段优先用 `class(Name).Member` 或已验证反射入口,不要裸写类名调用。
4. 构造函数默认保持 `public create`,不要把 `private` / `protected create` 当成会自动执行的构造函数。
5. 没有已验证代码块时不要发明对象/类写法。
## 必须记住的规则 ## 必须记住的规则
- 类定义统一按 `type Name = class ... end;` 写。 - 类定义统一按 `type Name = class ... end;` 写。
@ -57,7 +49,7 @@
- 工厂式 `self(0)` / `self(1)` 已验证可用;当前解释器没有通过 `self()` 这种无参工厂式写法。 - 工厂式 `self(0)` / `self(1)` 已验证可用;当前解释器没有通过 `self()` 这种无参工厂式写法。
- 已做双文件运行验证:如果类定义在 `unit` 的嵌套路径里,可以用 `CreateObject("Unit1.Class1.Class2")` 这种字符串路径创建对象。 - 已做双文件运行验证:如果类定义在 `unit` 的嵌套路径里,可以用 `CreateObject("Unit1.Class1.Class2")` 这种字符串路径创建对象。
- 已做双文件运行验证:可以写 `type MyNewClass = class(Unit1.Class1.Class2)` 直接继承单元里的嵌套类路径。 - 已做双文件运行验证:可以写 `type MyNewClass = class(Unit1.Class1.Class2)` 直接继承单元里的嵌套类路径。
- 已验证 `{$IFDEF ParentClassInUnit}` 为真,可用于探测“继承和构造单元中的类”能力是否可用。 - 当前环境里 `{$IFDEF ParentClassInUnit}` 为真,可用于探测“继承和构造单元中的类”能力是否可用。
- `private` / `protected` / `public` 已验证可用;类开头未写可见性时,成员默认按 `public` 处理。 - `private` / `protected` / `public` 已验证可用;类开头未写可见性时,成员默认按 `public` 处理。
- 同一个可见性段里后续没有切换关键字的成员,会沿用前一个可见性。 - 同一个可见性段里后续没有切换关键字的成员,会沿用前一个可见性。
- 外部访问 `private` / `protected` 字段或方法,会在执行时报对象成员访问错误。 - 外部访问 `private` / `protected` 字段或方法,会在执行时报对象成员访问错误。
@ -117,12 +109,6 @@ end;
- 说明在当前解释器里,顶层类声明可以放在松散语句之后 - 说明在当前解释器里,顶层类声明可以放在松散语句之后
- 同时也说明 `CreateObject("MyClass")` 可以解析到后面才出现的类声明 - 同时也说明 `CreateObject("MyClass")` 可以解析到后面才出现的类声明
代码块身份:已验证输出片段
```text
5
```
顶层函数后面也可以继续声明类: 顶层函数后面也可以继续声明类:
代码块身份:已验证可执行示例 代码块身份:已验证可执行示例
@ -1248,7 +1234,7 @@ end.
已验证运行结果: 已验证运行结果:
- `{$IFDEF ParentClassInUnit}` 输出 `1` - 当前环境里 `{$IFDEF ParentClassInUnit}` 输出 `1`
## 最小可编译示例 ## 最小可编译示例

View File

@ -20,14 +20,6 @@
- 多个 `unit` 里有同名接口时,未限定调用到底命中谁 - 多个 `unit` 里有同名接口时,未限定调用到底命中谁
- 怎样显式指定要调用哪个 `unit` 的接口 - 怎样显式指定要调用哪个 `unit` 的接口
## Agent unit/作用域判断流程
1. 先判断当前交付是 `.tsl` 可执行脚本、`.tsf` 扩展模块,还是 `unit` 文件。
2. `unit` 文件只描述可复用单元;脚本入口仍放在 `.tsl`
3. `uses` 必须放在普通语句之前;普通语句后不要再追加顶层 `uses`
4. 默认参数、接口段、实现段和作用域边界只照本页已验证形态写。
5. 没有已验证代码块时不要发明 unit/作用域写法。
## 必须记住的规则 ## 必须记住的规则
- `unit` 是完整的顶层主体;常见完整形态是 `unit Name; interface ... implementation ... end.` - `unit` 是完整的顶层主体;常见完整形态是 `unit Name; interface ... implementation ... end.`
@ -66,14 +58,6 @@ end.
- 上面这就是当前解释器可通过的最小完整 `unit` 骨架。 - 上面这就是当前解释器可通过的最小完整 `unit` 骨架。
- `unit` 文件结尾要用 `end.`,不是普通函数或类的 `end;` - `unit` 文件结尾要用 `end.`,不是普通函数或类的 `end;`
如果该 `unit` 被脚本 `uses DemoUnit;` 后调用 `WriteLn(Ping());`,可观察输出为:
代码块身份:已验证输出片段
```text
1
```
### 简写 `unit` ### 简写 `unit`
代码块身份:已验证可执行示例 代码块身份:已验证可执行示例

View File

@ -14,14 +14,6 @@
回答“`SetSysParam` / `GetSysParam` 怎样用、`SysParams[...]` 是什么、`#Func() with array(...)` 这种后缀环境调用在当前解释器里怎样写”。 回答“`SetSysParam` / `GetSysParam` 怎样用、`SysParams[...]` 是什么、`#Func() with array(...)` 这种后缀环境调用在当前解释器里怎样写”。
## Agent 运行时上下文判断流程
1. 先判断要操作系统参数、运行时上下文对象,还是 `with` 后缀调用。
2. 系统参数优先用本页已验证的 `SetSysParam` / `GetSysParam` / `SysParams[...]` 形态。
3. `#Func() with array(...)` 只作为运行时环境调用写法,不要套到普通本地函数。
4. 本地函数后缀 `with` 属于反例时不要照写。
5. 没有已验证代码块时不要发明运行时上下文写法。
## 必须记住的规则 ## 必须记住的规则
- TSL 有一组运行时系统参数;当前页只写已经实际验证过的通用写法。 - TSL 有一组运行时系统参数;当前页只写已经实际验证过的通用写法。
@ -53,13 +45,6 @@ end.
- 依次输出 `123`、`XYZ` - 依次输出 `123`、`XYZ`
代码块身份:已验证输出片段
```text
123
XYZ
```
`SysParams[...]` 直接读写: `SysParams[...]` 直接读写:
代码块身份:已验证可执行示例 代码块身份:已验证可执行示例

View File

@ -14,14 +14,6 @@
回答“哪些写法最容易凭直觉写出来,但在 TSL 里会编译失败、运行出错,或语义并不可靠”。 回答“哪些写法最容易凭直觉写出来,但在 TSL 里会编译失败、运行出错,或语义并不可靠”。
## Agent 常见误写判断流程
1. 先识别用户写法属于值、变量、函数、表达式、对象、unit、TS-SQL 还是外部调用误区。
2. 遇到本页反例时,不要修成相邻语言习惯,必须跳回对应语法页找已验证正例。
3. 反例只用于排错和避免误写,不作为可照写模板。
4. 修复时保留 `.tsl` / `.tsf` 文件模型判断,不要只改局部语句。
5. 没有已验证代码块时不要发明替代语法。
## 这页怎么用 ## 这页怎么用
- 先按主题扫一遍,再回到对应正文看正确写法。 - 先按主题扫一遍,再回到对应正文看正确写法。
@ -51,12 +43,6 @@ end;
a = 1; a = 1;
``` ```
代码块身份:已验证输出片段
```text
invalid statement
```
这会编译失败。正确页:见 [07_expressions_and_operators.md](07_expressions_and_operators.md) 这会编译失败。正确页:见 [07_expressions_and_operators.md](07_expressions_and_operators.md)
#### 3. 把字符串当成 0 基下标 #### 3. 把字符串当成 0 基下标
@ -103,23 +89,20 @@ WriteLn(Length(s));
这不要按 C 风格字符串去理解。当前解释器里这里输出 `3`,说明 `#0` 仍是字符串内容的一部分。正确页:见 [04_values_and_literals.md](04_values_and_literals.md) 这不要按 C 风格字符串去理解。当前解释器里这里输出 `3`,说明 `#0` 仍是字符串内容的一部分。正确页:见 [04_values_and_literals.md](04_values_and_literals.md)
#### 7. `.tsl` 声明区后面继续写脚本语句 #### 7. 把顶层函数定义和松散语句混写
代码块身份:反例 / 不可照写 代码块身份:反例 / 不可照写
```text ```text
a := 1; function Add(a, b);
test();
function test();
begin begin
echo "test"; return a + b;
end; end;
echo "after declaration"; value := Add(1, 2);
``` ```
`.tsl` 可以有语句区和声明区,但顺序必须清楚:语句区在前并按顺序执行,函数/类声明区在后。不要在声明区后面继续写脚本语句。正确页:见 [03_core_model.md](03_core_model.md) 这会编译失败。正确页:见 [03_core_model.md](03_core_model.md)
#### 8. 顶层单独写 `const Name = value;` #### 8. 顶层单独写 `const Name = value;`

View File

@ -14,14 +14,6 @@
回答“`array(...)` 在 TSL 里除了最普通的一维数组,还能怎样组织数据;哪些矩阵样写法已经稳定验证过”。 回答“`array(...)` 在 TSL 里除了最普通的一维数组,还能怎样组织数据;哪些矩阵样写法已经稳定验证过”。
## Agent 矩阵/集合判断流程
1. 先判断需要顺序数组、字符串键表、嵌套数组,还是矩阵样比较。
2. 普通集合优先从 `array(...)` 起手;下标和键访问回看值语法页。
3. 矩阵链式比较只照 `::` 系列已验证示例写,不要混用标量链式比较。
4. 集合过滤或 TS-SQL 查询需求跳转到结果集/TS-SQL 页面。
5. 没有已验证代码块时不要发明矩阵/集合写法。
## 必须记住的规则 ## 必须记住的规则
- `array(...)` 既可以写顺序数组,也可以写字符串键表。 - `array(...)` 既可以写顺序数组,也可以写字符串键表。
@ -50,15 +42,6 @@ WriteLn("HASH=", hash["Code"]);
- `arr[1] = 20` - `arr[1] = 20`
- `hash["Code"] = "0001"` - `hash["Code"] = "0001"`
代码块身份:已验证输出片段
```text
1
2
3
4
```
嵌套数组: 嵌套数组:
代码块身份:已验证可执行示例 代码块身份:已验证可执行示例

View File

@ -12,15 +12,7 @@
## 这一篇解决什么问题 ## 这一篇解决什么问题
回答“已有数组或二维结果集时,想按某个过滤集保留命中的行、排除命中的行,或者只拿到符合条件的行下标时,应该怎么写”。 回答“我已经有一个数组或二维结果集,想按某个过滤集保留命中的行、排除命中的行,或者只拿到符合条件的行下标时,应该怎么写”。
## Agent 结果集/过滤判断流程
1. 先判断任务是普通数组过滤、结果集字段访问,还是 TS-SQL 查询。
2. 字段访问优先照本页已验证的字符串键或结果集字段形态写。
3. 复杂查询需求优先跳转到 TS-SQL 页面,不要把业务过滤硬塞进基础表达式。
4. 金融业务筛选要回到 finance 层确认上下文。
5. 没有已验证代码块时不要发明结果集/过滤写法。
## 必须记住的规则 ## 必须记住的规则
@ -61,15 +53,6 @@ end.
- `FilterIn(R, CodeArr, "Code")` 返回两行:`0001`、`0003` - `FilterIn(R, CodeArr, "Code")` 返回两行:`0001`、`0003`
- `FilterNotIn(R, CodeArr, "Code")` 返回两行:`0002`、`0004` - `FilterNotIn(R, CodeArr, "Code")` 返回两行:`0002`、`0004`
代码块身份:已验证输出片段
```text
0001
0003
0002
0004
```
### 返回行下标 ### 返回行下标
第四个参数写成 `false` 时,返回行下标而不是子结果集: 第四个参数写成 `false` 时,返回行下标而不是子结果集:

View File

@ -14,14 +14,6 @@
回答“第一次进入 TS-SQL 时,应该先记住哪些固定骨架,怎样区分基础查询和高级查询,以及哪些内容属于语法、哪些已经属于业务层数据访问”。 回答“第一次进入 TS-SQL 时,应该先记住哪些固定骨架,怎样区分基础查询和高级查询,以及哪些内容属于语法、哪些已经属于业务层数据访问”。
## Agent TS-SQL 判断流程
1. 先判断是 TS-SQL 总览、核心查询,还是进阶查询。
2. 第一次落代码优先跳转到 `28_ts_sql_core.md` 的最小 `select ... from ... end` 骨架。
3. 需要 join、分组上下文或极值引用时再进入 `29_ts_sql_advanced.md`
4. 不要把 SQL 数据库语法直接当作 TS-SQL 语法。
5. 没有已验证代码块时不要发明 TS-SQL 写法。
## 必须记住的规则 ## 必须记住的规则
- TS-SQL 是 TSL 自带的类 SQL 查询语法,不是金融业务函数库。 - TS-SQL 是 TSL 自带的类 SQL 查询语法,不是金融业务函数库。
@ -46,7 +38,6 @@ begin
("A": 2, "B": 1) ("A": 2, "B": 1)
); );
R := select * from T end; R := select * from T end;
WriteLn(Length(R));
end. end.
``` ```
@ -56,12 +47,6 @@ end.
- 两行依次是 `(1,3)`、`(2,1)` - 两行依次是 `(1,3)`、`(2,1)`
- 说明 TS-SQL 的最短可靠入口就是“准备结果集,然后 `select ... from T end` - 说明 TS-SQL 的最短可靠入口就是“准备结果集,然后 `select ... from T end`
代码块身份:已验证输出片段
```text
2
```
### 四个查询入口怎样分工 ### 四个查询入口怎样分工
代码块身份:已验证可执行示例 代码块身份:已验证可执行示例

View File

@ -14,14 +14,6 @@
回答“`goto`、`DebugReturn`、`DebugRunEnv`、`MTIC` / `MTOC`、`SetProfiler`、`__line__` 和 `__stack_frame` 在当前解释器里怎样写、会怎样表现”。 回答“`goto`、`DebugReturn`、`DebugRunEnv`、`MTIC` / `MTOC`、`SetProfiler`、`__line__` 和 `__stack_frame` 在当前解释器里怎样写、会怎样表现”。
## Agent 调试/Profiler 判断流程
1. 先判断任务需要跳转、提前返回、运行环境调试、计时还是 profiler。
2. 普通控制流优先回到 `08_control_flow.md`,本页只处理调试补充工具。
3. `DebugReturn` 会结束整段脚本,不能当成普通函数返回。
4. 计时和 profiler 只照已验证最小调用写,不要补未验证参数。
5. 没有已验证代码块时不要发明调试/Profiler 写法。
## 必须记住的规则 ## 必须记住的规则
- `goto label_name;` 当前已验证可用,但目标位置当前以 `label label_name; statement` 这种内联形式最稳。 - `goto label_name;` 当前已验证可用,但目标位置当前以 `label label_name; statement` 这种内联形式最稳。

View File

@ -14,14 +14,6 @@
回答“TSL 的词法层规则和编译期开关应该去哪里查,而不是把这些边界混进值、函数、类的正文里”。 回答“TSL 的词法层规则和编译期开关应该去哪里查,而不是把这些边界混进值、函数、类的正文里”。
## Agent 词法/编译选项判断流程
1. 先判断要写注释、标识符、条件编译,还是编译选项。
2. 注释、大小写、条件编译指令只照本页已验证形态写。
3. `{$Explicit+}` 会改变变量声明要求,生成代码前先判断是否需要 `var`
4. `{$VarByRef+}` / `{$VarByRef-}` 会影响未修饰形参传递语义,细节回看函数页。
5. 没有已验证代码块时不要发明词法/编译选项写法。
## 必须记住的规则 ## 必须记住的规则
- 标识符大小写无关;当前最小运行验证里,下划线也可以出现在标识符中。 - 标识符大小写无关;当前最小运行验证里,下划线也可以出现在标识符中。
@ -55,13 +47,6 @@ end.
- 说明当前解释器下标识符大小写无关 - 说明当前解释器下标识符大小写无关
- 也说明下划线可以出现在标识符中 - 也说明下划线可以出现在标识符中
代码块身份:已验证输出片段
```text
7
7
```
注释与条件编译: 注释与条件编译:
代码块身份:已验证可执行示例 代码块身份:已验证可执行示例

View File

@ -14,14 +14,6 @@
回答“当问题不再是怎么写字面量,而是变量在运行时会变成什么类型、怎样转换、怎样受编译选项影响时,应该看哪里”。 回答“当问题不再是怎么写字面量,而是变量在运行时会变成什么类型、怎样转换、怎样受编译选项影响时,应该看哪里”。
## Agent 类型/转换判断流程
1. 先判断要写数值字面量、日期时间、真假值、nil还是类型转换边界。
2. 整数、实数、日期时间和特殊实数只照本页已验证字面量写。
3. 不要把能编译的混合类型表达式误判为能按预期自动转换。
4. 遇到字符串细节跳转到 `20_strings_and_text.md`,遇到值基础跳转到 `04_values_and_literals.md`
5. 没有已验证代码块时不要发明类型/转换写法。
## 必须记住的规则 ## 必须记住的规则
- 当前解释器接受十进制、`0x` 十六进制、`0b` 二进制、`0o` 八进制整数常量。 - 当前解释器接受十进制、`0x` 十六进制、`0b` 二进制、`0o` 八进制整数常量。
@ -73,14 +65,6 @@ end.
- 说明 `100L` 在当前解释器里按 `Int64` 处理 - 说明 `100L` 在当前解释器里按 `Int64` 处理
- 也说明 `1E2` 当前按实数,不按整数处理 - 也说明 `1E2` 当前按实数,不按整数处理
代码块身份:已验证输出片段
```text
1
0
1
```
超 32 位整数与日期时间字面量: 超 32 位整数与日期时间字面量:
代码块身份:已验证可执行示例 代码块身份:已验证可执行示例

View File

@ -14,14 +14,6 @@
回答“当任务进入字符串和编码细节,而不是只写普通字面量时,应该进入哪一篇”。 回答“当任务进入字符串和编码细节,而不是只写普通字面量时,应该进入哪一篇”。
## Agent 字符串/文本判断流程
1. 先判断要写普通字符串、宽串/UTF8 前缀、原始字符串,还是字符串边界行为。
2. 默认优先普通字符串;只有编码或宽串需求明确时才使用 `L""` / `U""`
3. 字符串索引、切片、转义和 `#0` 行为只照已验证示例写。
4. 字符串表达式行为回看表达式页,不要在文本页发明运算规则。
5. 没有已验证代码块时不要发明字符串/文本写法。
## 必须记住的规则 ## 必须记住的规则
- 普通字符串、`L""` 宽串、`U""` UTF8 前缀串,以及 `%%` 原始字符串,当前解释器都接受。 - 普通字符串、`L""` 宽串、`U""` UTF8 前缀串,以及 `%%` 原始字符串,当前解释器都接受。
@ -64,15 +56,6 @@ end.
- 说明 `%%tag ...%%tag` 可以用标识符配对 - 说明 `%%tag ...%%tag` 可以用标识符配对
- 也说明 `%%` 原始字符串支持直接跨行 - 也说明 `%%` 原始字符串支持直接跨行
代码块身份:已验证输出片段
```text
ABC
A"B'C
A
B
```
带前缀的原始字符串: 带前缀的原始字符串:
代码块身份:已验证可执行示例 代码块身份:已验证可执行示例

View File

@ -14,14 +14,6 @@
回答“普通函数怎么写已经清楚后,外部 DLL、函数指针、多线程这些系统交互能力应该去哪里查”。 回答“普通函数怎么写已经清楚后,外部 DLL、函数指针、多线程这些系统交互能力应该去哪里查”。
## Agent 外部调用/线程判断流程
1. 先判断要声明外部函数、绑定函数指针,还是处理线程相关能力。
2. 外部函数声明只照已验证的调用约定、`external` 和可选 `name` 形态写。
3. DLL 名优先写字面量或已验证类常量,不要拼接表达式。
4. 函数指针包装和线程相关能力只照本页已验证边界写。
5. 没有已验证代码块时不要发明外部调用/线程写法。
## 必须记住的规则 ## 必须记住的规则
- 当前解释器接受 `function Name(...): Type; stdcall|cdecl; external "dll" [name "symbol"];` 这类外部函数声明。 - 当前解释器接受 `function Name(...): Type; stdcall|cdecl; external "dll" [name "symbol"];` 这类外部函数声明。
@ -32,7 +24,6 @@
- 当前已验证的 DLL 名写法包括字面量字符串和类常量字符串;不要把字符串拼接表达式直接当成稳定写法。 - 当前已验证的 DLL 名写法包括字面量字符串和类常量字符串;不要把字符串拼接表达式直接当成稳定写法。
- `MakeInstance(ThisFunction(Func), "cdecl", 0)` 当前可以把 TSL 函数包装成 C 调用约定函数指针。 - `MakeInstance(ThisFunction(Func), "cdecl", 0)` 当前可以把 TSL 函数包装成 C 调用约定函数指针。
- `MakeInstance(..., "cdecl", 1)` 当前在 `Win64` 下已经拿到最小线程正例,可以直接交给 `CreateThread` - `MakeInstance(..., "cdecl", 1)` 当前在 `Win64` 下已经拿到最小线程正例,可以直接交给 `CreateThread`
- 外部库名和调用约定要按目标平台选择;不要把 Windows 的 `kernel32.dll` 示例直接复制到 Linux或把 Linux 的 `.so` 示例直接复制到 Windows。
- 当前页的线程示例是 Windows 专题,用到了 `kernel32.dll` - 当前页的线程示例是 Windows 专题,用到了 `kernel32.dll`
## 已验证语法 ## 已验证语法
@ -55,32 +46,6 @@ end.
- 说明当前解释器接受 `stdcall` + `external "dll" name "symbol"` 这类外部函数声明骨架 - 说明当前解释器接受 `stdcall` + `external "dll" name "symbol"` 这类外部函数声明骨架
- 也说明 TSL 里的函数名可以和 DLL 导出名不同,再通过 `name "ExportName"` 绑定 - 也说明 TSL 里的函数名可以和 DLL 导出名不同,再通过 `name "ExportName"` 绑定
代码块身份:已验证输出片段
```text
1
```
Linux / POSIX 环境的同类最小骨架:
代码块身份:已验证可执行示例
```tsl
program test;
function getpid(): integer; cdecl; external "libc.so.6";
begin
WriteLn(getpid() > 0);
end.
```
代码块身份:已验证输出片段
```text
1
```
这段只说明 Linux / POSIX 目标下可以用 `.so` 库名声明外部函数;生成代码时仍要先判断用户的目标平台。
当本地函数名和 DLL 导出名一致时,`name` 可以省略: 当本地函数名和 DLL 导出名一致时,`name` 可以省略:
代码块身份:已验证可执行示例 代码块身份:已验证可执行示例

View File

@ -21,14 +21,6 @@
- `NameSpace "..."`、`tsl.conf` 和 `-LIBPATH` 怎样影响 `.tsf` 查找 - `NameSpace "..."`、`tsl.conf` 和 `-LIBPATH` 怎样影响 `.tsf` 查找
- `syssettsllibpath()` / `sysgettsllibpath()` 怎样在运行时改查找路径 - `syssettsllibpath()` / `sysgettsllibpath()` 怎样在运行时改查找路径
## Agent 命名空间/Libpath 判断流程
1. 先判断任务是单元命名空间访问、`-LIBPATH` 查找,还是配置文件查找。
2. 路径和查找顺序只写平台中立规则,不写某个开发机或容器路径。
3. `-LIBPATH` 只作为运行时部署/查找规则,不写进普通语法示例。
4. 未验证的路径替换和父目录继承不要当成稳定规则。
5. 没有已验证代码块时不要发明命名空间/Libpath 写法。
## 必须记住的规则 ## 必须记住的规则
- 完整 `unit` 形态可以包含 `interface`、`implementation`、`initialization`、`finalization`,并以 `end.` 结束。 - 完整 `unit` 形态可以包含 `interface`、`implementation`、`initialization`、`finalization`,并以 `end.` 结束。
@ -94,18 +86,6 @@ end.
- 这说明顶层写了 `uses DemoUnit;` 之后,`initialization` 不是立刻执行,而是在第一次真正读 `DemoUnit` 成员时触发。 - 这说明顶层写了 `uses DemoUnit;` 之后,`initialization` 不是立刻执行,而是在第一次真正读 `DemoUnit` 成员时触发。
- `finalization` 在脚本主体输出结束后触发。 - `finalization` 在脚本主体输出结束后触发。
代码块身份:已验证输出片段
```text
BEFORE
INIT
7
1
101
11
FINAL
```
### `unit` 成员的读取边界 ### `unit` 成员的读取边界
直接限定读取: 直接限定读取:

View File

@ -14,14 +14,6 @@
回答“类已经会声明、继承、构造之后,怎样检查类信息、函数信息和对象运行时状态”。 回答“类已经会声明、继承、构造之后,怎样检查类信息、函数信息和对象运行时状态”。
## Agent 对象运行时/反射判断流程
1. 先判断要做类查找、函数查找、对象检查,还是运行时成员访问。
2. 反射入口优先照 `FindClass`、`FindFunction` 等已验证示例写。
3. 对象模型本身回看 `09_objects_and_classes.md`,不要在运行时页发明类声明语法。
4. 函数值调用边界回看函数页,避免把函数指针直接当普通函数调用。
5. 没有已验证代码块时不要发明对象运行时/反射写法。
## 必须记住的规则 ## 必须记住的规则
- 对象值当前可以用 `ifObj(...)` 做显式判定。 - 对象值当前可以用 `ifObj(...)` 做显式判定。
@ -60,12 +52,6 @@ end.
- 输出 `1` - 输出 `1`
- 说明 `CreateObject(...)` 得到的对象值当前可以用 `ifObj(...)` 判定 - 说明 `CreateObject(...)` 得到的对象值当前可以用 `ifObj(...)` 判定
代码块身份:已验证输出片段
```text
1
```
`FindClass(...)` 与类类型变量: `FindClass(...)` 与类类型变量:
代码块身份:已验证可执行示例 代码块身份:已验证可执行示例

View File

@ -14,14 +14,6 @@
回答“写普通 TSL/TSF 脚本时,哪些内置对象可以直接创建,最小可用接口是什么,哪些对象其实依赖特定运行上下文”。 回答“写普通 TSL/TSF 脚本时,哪些内置对象可以直接创建,最小可用接口是什么,哪些对象其实依赖特定运行上下文”。
## Agent 内置运行时对象判断流程
1. 先判断要访问哪个内置运行时对象,以及它是不是语言对象而非业务 API。
2. 只照本页已验证的最小读写路径使用内置对象。
3. 不要把金融业务上下文对象和语言运行时对象混在一起。
4. 需要系统参数或 `with` 后缀时跳转到运行时上下文页。
5. 没有已验证代码块时不要发明内置运行时对象写法。
## 必须记住的规则 ## 必须记住的规则
- 当前 CLI 里,`TStringList` 已验证可以直接 `CreateObject("TStringList")` 创建。 - 当前 CLI 里,`TStringList` 已验证可以直接 `CreateObject("TStringList")` 创建。
@ -58,15 +50,6 @@ end.
- 依次输出 `B=bbb`、`bbb`、`3`、`4` - 依次输出 `B=bbb`、`bbb`、`3`、`4`
- 说明当前既可以按数字下标取整行,也可以按键名取 `Name=Value` 里的值 - 说明当前既可以按数字下标取整行,也可以按键名取 `Name=Value` 里的值
代码块身份:已验证输出片段
```text
B=bbb
bbb
3
4
```
### `TStream` 家族的最小可靠入口:`TMemoryStream` ### `TStream` 家族的最小可靠入口:`TMemoryStream`
代码块身份:已验证可执行示例 代码块身份:已验证可执行示例

View File

@ -14,14 +14,6 @@
回答“某个元素是否在数组里、某组元素是否构成子集、某一行是否存在于结果集中,以及两个结果集如何做并集、交集、差集和对称差集”。 回答“某个元素是否在数组里、某组元素是否构成子集、某一行是否存在于结果集中,以及两个结果集如何做并集、交集、差集和对称差集”。
## Agent 集合运算判断流程
1. 先判断要写 `in`、`sqlin`、否定集合判断,还是结果集集合操作。
2. 基础成员判断优先照表达式页和本页已验证示例写。
3. `not in``not sqlin` 是已验证否定写法,不要改成未验证组合。
4. 复杂查询或矩阵数据跳转到 TS-SQL / 矩阵页面。
5. 没有已验证代码块时不要发明集合运算写法。
## 必须记住的规则 ## 必须记住的规则
- `in` / `not in` 处理的是元素存在关系,以及左侧为数组时的子集关系。 - `in` / `not in` 处理的是元素存在关系,以及左侧为数组时的子集关系。
@ -62,15 +54,6 @@ end.
- `array(1, 2) in array(1)` 输出 `0` - `array(1, 2) in array(1)` 输出 `0`
- `1 not in array(0, 2)` 输出 `1` - `1 not in array(0, 2)` 输出 `1`
代码块身份:已验证输出片段
```text
1
0
1
1
```
### `sqlin``not sqlin` ### `sqlin``not sqlin`
`sqlin` 改成按整行判断左侧是否存在于右侧结果集中: `sqlin` 改成按整行判断左侧是否存在于右侧结果集中:

View File

@ -14,14 +14,6 @@
回答“怎样直接构造全零矩阵、全一矩阵、随机矩阵、单位矩阵、空矩阵和数列数组,以及怎样拿到矩阵的行数、列数、行索引和列索引”。 回答“怎样直接构造全零矩阵、全一矩阵、随机矩阵、单位矩阵、空矩阵和数列数组,以及怎样拿到矩阵的行数、列数、行索引和列索引”。
## Agent 矩阵深水判断流程
1. 先判断要读矩阵尺寸、列索引、矩阵选择,还是矩阵与数组转换。
2. 基础数组和矩阵样比较先回看 `13_matrix_and_collections.md`
3. `MRows` / `MCols` / `MSize` 等函数只照已验证返回形态写。
4. 不要把列索引数组误当成单个数字。
5. 没有已验证代码块时不要发明矩阵深水写法。
## 必须记住的规则 ## 必须记住的规则
- `Zeros(...)`、`Ones(...)`、`Rand(...)`、`Nils(...)`、`Eye(...)` 都可以直接用于矩阵初始化。 - `Zeros(...)`、`Ones(...)`、`Rand(...)`、`Nils(...)`、`Eye(...)` 都可以直接用于矩阵初始化。
@ -50,9 +42,6 @@ begin
E1 := Eye(3); E1 := Eye(3);
R1 := Rand(2, 3); R1 := Rand(2, 3);
T1 := Zeros(2, array("A", "B")); T1 := Zeros(2, array("A", "B"));
WriteLn(Length(Z1));
WriteLn(MRows(Z2));
WriteLn(MCols(Z2));
end. end.
``` ```
@ -66,14 +55,6 @@ end.
- `Rand(2, 3)` 的行数是 `2`、列数是 `3` - `Rand(2, 3)` 的行数是 `2`、列数是 `3`
- `Zeros(2, array("A", "B"))` 的行数是 `2`、列数是 `2`,并且 `T1[0]["A"]`、`T1[0]["B"]`、`T1[1]["A"]`、`T1[1]["B"]` 都是 `0` - `Zeros(2, array("A", "B"))` 的行数是 `2`、列数是 `2`,并且 `T1[0]["A"]`、`T1[0]["B"]`、`T1[1]["A"]`、`T1[1]["B"]` 都是 `0`
代码块身份:已验证输出片段
```text
3
2
3
```
### `->` 数列数组初始化 ### `->` 数列数组初始化
默认步长为 `1` 默认步长为 `1`

View File

@ -14,14 +14,6 @@
回答“什么时候该用 `FMArray` 而不是普通 `array`,以及当前解释器里最可靠的 `FMArray` 写法到底有哪些”。 回答“什么时候该用 `FMArray` 而不是普通 `array`,以及当前解释器里最可靠的 `FMArray` 写法到底有哪些”。
## Agent FMArray 判断流程
1. 先判断是否确实需要 `FMArray`,普通数组能解决时先用普通数组。
2. 构造、类型判断、尺寸读取、转置、连接和 TS-SQL 参与只照本页已验证形态写。
3. `insert`、`delete`、`update` 的收尾形式分别判断,不要互相套用。
4. FMArray 错误边界按本页反例处理,不要凭普通数组经验修写法。
5. 没有已验证代码块时不要发明 FMArray 写法。
## 必须记住的规则 ## 必须记住的规则
- `fmarray[...]` 可以直接构造 `FMArray` 常量。 - `fmarray[...]` 可以直接构造 `FMArray` 常量。
@ -74,15 +66,6 @@ end.
- `f2` 的行数是 `2`、列数是 `2` - `f2` 的行数是 `2`、列数是 `2`
- `f2` 四个单元依次是 `1`、`2`、`3`、`4` - `f2` 四个单元依次是 `1`、`2`、`3`、`4`
代码块身份:已验证输出片段
```text
27
0
1
1
```
### `MInit`、`MInitDiag`、`MRand` ### `MInit`、`MInitDiag`、`MRand`
代码块身份:已验证可执行示例 代码块身份:已验证可执行示例
@ -533,7 +516,7 @@ WriteLn(f["A"]);
delete from d where [1] = 4 end; delete from d where [1] = 4 end;
``` ```
这类写法不要直接当成当前默认模板。当前已验证结果显示,`delete ... end;` 会报 `Statement missing terminator`;已验证可用的是 `delete ...;` 这类写法不要直接当成当前默认模板。当前解释器下,我实测 `delete ... end;` 会报 `Statement missing terminator`;已验证可用的是 `delete ...;`
代码块身份:反例 / 不可照写 代码块身份:反例 / 不可照写

View File

@ -14,14 +14,6 @@
回答“第一次写 TS-SQL 时,怎样在不碰 join、数据库表和对象化写回接口的前提下稳定写出最小查询”。 回答“第一次写 TS-SQL 时,怎样在不碰 join、数据库表和对象化写回接口的前提下稳定写出最小查询”。
## Agent TS-SQL Core 判断流程
1. 先判断要写 `select`、`sselect`、`vselect` 还是 `mselect`
2. 内存数组查询优先从 `select ... from T end` 最小骨架起手。
3. 二维结果集字段访问用 `["字段名"]`,一维数组优先用 `ThisRow``ThisRowIndex`
4. join 和进阶聚合跳转到 `29_ts_sql_advanced.md`,不要在核心页发明写法。
5. 没有已验证代码块时不要发明 TS-SQL Core 写法。
## 必须记住的规则 ## 必须记住的规则
- TS-SQL 查询以 `select`、`sselect`、`vselect` 或 `mselect` 开始,以 `end` 结束。 - TS-SQL 查询以 `select`、`sselect`、`vselect` 或 `mselect` 开始,以 `end` 结束。
@ -51,7 +43,6 @@ begin
("A": 1, "B": 2) ("A": 1, "B": 2)
); );
R := select ["A"], ["B"] from T end; R := select ["A"], ["B"] from T end;
WriteLn(Length(R));
end. end.
``` ```
@ -61,12 +52,6 @@ end.
- 三行依次是 `(1,3)`、`(2,1)`、`(1,2)` - 三行依次是 `(1,3)`、`(2,1)`、`(1,2)`
- 说明 `select ["A"], ["B"] from T end` 会按原顺序返回二维结果集 - 说明 `select ["A"], ["B"] from T end` 会按原顺序返回二维结果集
代码块身份:已验证输出片段
```text
3
```
`select *` 也已验证可用: `select *` 也已验证可用:
代码块身份:已验证可执行示例 代码块身份:已验证可执行示例
@ -234,7 +219,7 @@ R := select A from T end;
R := select [0] from X end; R := select [0] from X end;
``` ```
当前已验证结果显示,这种对一维数组直接用 `[0]` 的写法虽然返回了长度为 `3` 的结果,但取到的值是 `nil`,不能当成可靠入口。对一维数组应改用 `ThisRow``ThisRowIndex` 我在 `2026-04-15` 的实测里,这种对一维数组直接用 `[0]` 的写法虽然返回了长度为 `3` 的结果,但取到的值是 `nil`,不能当成可靠入口。对一维数组应改用 `ThisRow``ThisRowIndex`
## 跳转指引 ## 跳转指引

View File

@ -14,14 +14,6 @@
回答“基础 `select` 已经会写以后,怎样继续处理多表联接、分组后组内再查、以及极值对应行的引用值”。 回答“基础 `select` 已经会写以后,怎样继续处理多表联接、分组后组内再查、以及极值对应行的引用值”。
## Agent TS-SQL Advanced 判断流程
1. 先确认核心 `select ... from ... end` 已能表达基础查询,再进入进阶语法。
2. 多表 join、`ThisGroup`、`ThisRowIndex` 和极值引用只照本页已验证示例写。
3. 字段访问和返回形态仍遵守核心 TS-SQL 页规则。
4. 不要把数据库 SQL 方言直接迁移到 TS-SQL。
5. 没有已验证代码块时不要发明 TS-SQL Advanced 写法。
## 必须记住的规则 ## 必须记住的规则
- 多表 `join` 时,字段访问应写成 `[表序号].["字段名"]` - 多表 `join` 时,字段访问应写成 `[表序号].["字段名"]`
@ -52,10 +44,6 @@ begin
R := select [1].["ID"], [1].["V1"], [2].["V2"] R := select [1].["ID"], [1].["V1"], [2].["V2"]
from A join B on [1].["ID"] = [2].["ID"] from A join B on [1].["ID"] = [2].["ID"]
end; end;
WriteLn(Length(R));
WriteLn(R[0]["ID"]);
WriteLn(R[0]["V1"]);
WriteLn(R[0]["V2"]);
end. end.
``` ```
@ -65,15 +53,6 @@ end.
- 唯一一行是 `(1,10,100)` - 唯一一行是 `(1,10,100)`
- 说明当前解释器接受 `from A join B on ...`,并接受 `[1].["字段"]`、`[2].["字段"]` 这种多表字段访问 - 说明当前解释器接受 `from A join B on ...`,并接受 `[1].["字段"]`、`[2].["字段"]` 这种多表字段访问
代码块身份:已验证输出片段
```text
1
1
10
100
```
### `ThisGroup` ### `ThisGroup`
分组后可在组内继续做子查询: 分组后可在组内继续做子查询:

View File

@ -14,14 +14,6 @@
回答“网格调用现在怎样写,全局缓存最稳的读写方式是什么,以及什么时候缓存值会失效或脱离缓存身份”。 回答“网格调用现在怎样写,全局缓存最稳的读写方式是什么,以及什么时候缓存值会失效或脱离缓存身份”。
## Agent 运行时服务/全局缓存判断流程
1. 先判断要用 `#` 网格调用、`timeout` 后缀,还是全局缓存函数。
2. 运行时服务只照本页已验证最小路径写,不要猜测隐藏参数。
3. 全局缓存读写要成对出现,并明确 key/value 生命周期。
4. 普通函数调用回到函数页,不要把运行时服务写成普通语法糖。
5. 没有已验证代码块时不要发明运行时服务/全局缓存写法。
## 必须记住的规则 ## 必须记住的规则
- 网格调用的最小写法是 `r := #Func(args);` - 网格调用的最小写法是 `r := #Func(args);`
@ -56,12 +48,6 @@ end.
- `#AddOne(5)` 可以执行 - `#AddOne(5)` 可以执行
- `dupvalue(r)` 返回最终结果 `6` - `dupvalue(r)` 返回最终结果 `6`
代码块身份:已验证输出片段
```text
6
```
### 网格调用的 `timeout` ### 网格调用的 `timeout`
代码块身份:已验证可执行示例 代码块身份:已验证可执行示例

View File

@ -14,14 +14,6 @@
回答“复数现在到底怎样写,弱引用到底有哪些语法真的能用,哪些历史资料里的写法今天不能直接照抄”。 回答“复数现在到底怎样写,弱引用到底有哪些语法真的能用,哪些历史资料里的写法今天不能直接照抄”。
## Agent 复数/弱引用判断流程
1. 先判断任务需要复数,还是弱引用/自动弱引用。
2. 复数字面量和比较只照本页已验证示例写。
3. 访问弱引用前先做 `checkweakref(...)` 判定,不要假设失效弱引用安全返回 nil。
4. 类内 `WeakRef;` / `AutoRef;` 段落式写法属于反例,不要照写。
5. 没有已验证代码块时不要发明复数/弱引用写法。
## 必须记住的规则 ## 必须记住的规则
- 复数字面量可以直接写成 `a + bj`,也可以用 `complex(a, b)` 构造。 - 复数字面量可以直接写成 `a + bj`,也可以用 `complex(a, b)` 构造。
@ -64,15 +56,6 @@ end.
- `imag(4 + 3j)` 返回 `3` - `imag(4 + 3j)` 返回 `3`
- `complex(5, -2)` 打印结果是 `5-2j` - `complex(5, -2)` 打印结果是 `5-2j`
代码块身份:已验证输出片段
```text
41
1
4
3
```
### 共轭、模与等值比较 ### 共轭、模与等值比较
代码块身份:已验证可执行示例 代码块身份:已验证可执行示例

View File

@ -14,14 +14,6 @@
回答“当类不只是普通对象,而要直接参与 `obj + x`、`obj[index]`、`for v in obj`、`mrows(obj)` 这类语言级操作时,当前解释器到底支持哪些真实写法”。 回答“当类不只是普通对象,而要直接参与 `obj + x`、`obj[index]`、`for v in obj`、`mrows(obj)` 这类语言级操作时,当前解释器到底支持哪些真实写法”。
## Agent 对象重载/迭代判断流程
1. 先判断要重载二元算符、下标、`for in`,还是矩阵尺寸函数。
2. 对象重载只照本页已验证的 `operator` 签名写,不要从历史资料扩展未知重载。
3. 普通对象模型先回看 `09_objects_and_classes.md`,不要在重载页发明类基础语法。
4. 未列入本页主干的重载族先视为未验证。
5. 没有已验证代码块时不要发明对象重载/迭代写法。
## 必须记住的规则 ## 必须记住的规则
- 对象二元算符重载当前最小可靠形态是成员方法 `function operator + (data);` 这一类写法。 - 对象二元算符重载当前最小可靠形态是成员方法 `function operator + (data);` 这一类写法。
@ -86,15 +78,6 @@ end.
- 说明 `obj + value` 当前可以通过成员 `operator +` 接管 - 说明 `obj + value` 当前可以通过成员 `operator +` 接管
- 说明带 `isLeft` 的比较算符当前可以同时处理 `obj < value``value < obj` - 说明带 `isLeft` 的比较算符当前可以同时处理 `obj < value``value < obj`
代码块身份:已验证输出片段
```text
20
0
1
1
```
### `[]` 重载:`operator[]` / `operator[1]` ### `[]` 重载:`operator[]` / `operator[1]`
代码块身份:已验证可执行示例 代码块身份:已验证可执行示例

View File

@ -6,7 +6,7 @@
是否含已验证反例:否 是否含已验证反例:否
遇到不确定时跳转到:[02_quickstart.md](02_quickstart.md)(优先)、[03_core_model.md](03_core_model.md)、[07_expressions_and_operators.md](07_expressions_and_operators.md)、[09_objects_and_classes.md](09_objects_and_classes.md)、[12_pitfalls.md](12_pitfalls.md) 遇到不确定时跳转到:[02_quickstart.md](02_quickstart.md)(优先)、[03_core_model.md](03_core_model.md)、[07_expressions_and_operators.md](07_expressions_and_operators.md)、[09_objects_and_classes.md](09_objects_and_classes.md)、[12_pitfalls.md](12_pitfalls.md)
这里是 TSL 语法手册入口。只处理“agent 应该怎样识别并生成语言写”,不处理金融业务语义。本文档不是人类教程;它给 agent 提供判断顺序、关键词分流和可靠示例入口。 这里是 TSL 语法手册入口。只处理“语言怎么写”,不处理金融业务语义。
## 元数据读法 ## 元数据读法
@ -20,20 +20,10 @@
## 这本手册怎么读 ## 这本手册怎么读
- 默认先走“Agent 判断流程”和“按任务跳转”,不要先顺序读完整套。 - 默认先走“按任务跳转”,不要先顺序读完整套。
- 只有刻意系统学习时,才按下面的“推荐读法”顺序进入。 - 只有刻意系统学习时,才按下面的“推荐读法”顺序进入。
- 当前这套正式语法手册不再只覆盖主线入门,而是负责完整吸收旧 8 个语言章节。 - 当前这套正式语法手册不再只覆盖主线入门,而是负责完整吸收旧 8 个语言章节。
## Agent 判断流程
1. 先识别用户是否已经给出 `.tsl` / `.tsf` 后缀;给出后缀时,后缀就是判断依据。
2. 未给后缀时,再按交付目标判断:可执行代码对应 `.tsl`,通用模块对应 `.tsf`;仍不明确时向用户确认。
3. 如果是 `.tsl`,按可执行脚本处理:先写语句区,语句按顺序执行;需要函数/类时,把声明区放在语句区之后。
4. 如果是 `.tsf`,按模块/函数扩展处理:生成可部署到解释器 `funcext` 的声明;部署后脚本可以直接调用暴露的顶层函数。
5. 再识别关键词属于值/变量、函数、类、unit、表达式、控制流、运行时、TS-SQL 或其他专题。
6. 只进入关键词命中的最小页面;写代码前优先复制已验证可执行示例的结构,不要从多个页面拼接未经验证的新骨架。
7. 遇到反例、解释器差异或资料冲突时,先按 [12_pitfalls.md](12_pitfalls.md) 和对应专题页收口;仍无结论时不要发明语法。
## 如果你马上要写 TSL ## 如果你马上要写 TSL
1. [02_quickstart.md](02_quickstart.md)(先看语言核心事实速查和最短骨架) 1. [02_quickstart.md](02_quickstart.md)(先看语言核心事实速查和最短骨架)

View File

@ -16,22 +16,10 @@
## TSL 核心约定(不可违反) ## TSL 核心约定(不可违反)
- 用户已给出 `.tsl` / `.tsf` 后缀时,后缀就是判断依据;未给后缀时,再根据用户交付目标判断。 - 文件结构:一文件一顶层声明;文件名 = 声明名;`.tsl` 仅 `function``.tsf` 可 `function/class/unit`
- `.tsl` 是可执行脚本:语句区按顺序执行;如果需要函数/类,放在脚本语句后的函数/类声明区。
- `.tsf` 是模块/函数扩展文件:部署到解释器 `funcext` 后,脚本可直接调用其中暴露的顶层函数。
- 格式4 空格缩进;关键字小写;多语句用 `begin/end` - 格式4 空格缩进;关键字小写;多语句用 `begin/end`
- 命名:类型/函数/property `PascalCase`;变量/参数 `snake_case`;私有 `snake_case_`;常量 `kPascalCase` - 命名:类型/函数/property `PascalCase`;变量/参数 `snake_case`;私有 `snake_case_`;常量 `kPascalCase`
## agent 判断流程
1. 用户已给出 `.tsl` / `.tsf` 后缀时,后缀就是判断依据。
2. 用户未给后缀时,先判断交付目标是可执行代码还是通用模块;可执行代码对应 `.tsl`,通用模块对应 `.tsf`
3. 交付目标仍不明确时向用户确认,不要替用户发明文件形态。
4. 写 `.tsl` 时,先生成顺序执行的语句区;调用本脚本内函数时,函数声明写在语句区之后。
5. 写 `.tsf` 时,只生成模块/扩展声明;顶层函数部署到解释器 `funcext` 后才可被脚本直接调用。
6. 如果关键词命中业务、函数库、模块集成或项目执行信息,按 `docs/tsl/index.md` 分流,不把所有问题都当语法问题。
7. 不确定时先找已验证可执行示例、反例页和对应专题页;没有文档结论时不要发明语法,改为向用户确认或记录文档缺口。
## 安全红线(不可触碰) ## 安全红线(不可触碰)
- 不得在代码/日志/注释中写入明文密钥、密码、Token、API Key - 不得在代码/日志/注释中写入明文密钥、密码、Token、API Key

View File

@ -1,11 +1,8 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
from contextlib import contextmanager from contextlib import contextmanager
from datetime import datetime, timezone
import getpass
import os import os
import platform import platform
import re import re
import socket
import sys import sys
import threading import threading
import time import time
@ -34,22 +31,6 @@ PLAN_LINE_RE = re.compile(
) )
FINISH_STATUSES = {"done", "blocked", "skipped"} FINISH_STATUSES = {"done", "blocked", "skipped"}
ENV_BLOCKED_RE = re.compile(r"^env:([^:]+):(.+)$") ENV_BLOCKED_RE = re.compile(r"^env:([^:]+):(.+)$")
PLAN_META_REQUIRED_FIELDS = (
"Plan Group",
"Parent Plan",
"Verification Scope",
"Verification Gate",
)
WORKFLOW_STATE_KEYS = {
"phase",
"spec",
"plan",
"executor",
"constraints",
"claimed_by",
"claimed_at",
"verification",
}
WORKFLOW_PHASES = { WORKFLOW_PHASES = {
"brainstorming", "brainstorming",
"planning", "planning",
@ -67,8 +48,7 @@ def usage() -> str:
"Usage:\n" "Usage:\n"
" python scripts/main_loop.py claim -plans <dir> -progress <file>\n" " python scripts/main_loop.py claim -plans <dir> -progress <file>\n"
" python scripts/main_loop.py finish -plan <path> -status <status> " " python scripts/main_loop.py finish -plan <path> -status <status> "
"-progress <file> [-note <text>] [-verified <text>]\n" "-progress <file> [-note <text>]\n"
" python scripts/main_loop.py status -plans <dir> -progress <file>\n"
" python scripts/main_loop.py record -progress <file> -phase <phase> " " python scripts/main_loop.py record -progress <file> -phase <phase> "
"[-spec <path>] [-plan <path>] [-executor <name>] " "[-spec <path>] [-plan <path>] [-executor <name>] "
"[-constraints <csv>]\n" "[-constraints <csv>]\n"
@ -83,8 +63,6 @@ def usage() -> str:
" -executor NAME\n" " -executor NAME\n"
" -constraints CSV\n" " -constraints CSV\n"
" -note TEXT\n" " -note TEXT\n"
" -owner NAME\n"
" -verified TEXT\n"
" -h, -help Show this help.\n" " -h, -help Show this help.\n"
) )
@ -120,24 +98,6 @@ def normalize_note(note: str) -> str:
return note.replace("\n", " ").replace("\r", " ").replace("`", "'").strip() return note.replace("\n", " ").replace("\r", " ").replace("`", "'").strip()
def now_utc() -> str:
return datetime.now(timezone.utc).replace(microsecond=0).isoformat().replace(
"+00:00", "Z"
)
def default_claim_owner() -> str:
owner = os.environ.get("PLAYBOOK_MAIN_LOOP_OWNER")
if owner:
return normalize_note(owner)
host = socket.gethostname() or "unknown-host"
try:
user = getpass.getuser()
except Exception: # pragma: no cover
user = "unknown-user"
return f"{user}@{host}:{os.getpid()}"
def render_plan_line(plan_key: str, status: str, note: Optional[str]) -> str: def render_plan_line(plan_key: str, status: str, note: Optional[str]) -> str:
checked = "x" if status == "done" else " " checked = "x" if status == "done" else " "
suffix = status suffix = status
@ -228,23 +188,18 @@ def render_workflow_state_lines(
plan: Optional[str] = None, plan: Optional[str] = None,
executor: Optional[str] = None, executor: Optional[str] = None,
constraints: Optional[str] = None, constraints: Optional[str] = None,
claimed_by: Optional[str] = None,
claimed_at: Optional[str] = None,
verification: Optional[str] = None,
) -> list[str]: ) -> list[str]:
lines = [WORKFLOW_STATE_START] lines = [WORKFLOW_STATE_START]
for key, value in ( if phase:
("phase", phase), lines.append(f"phase: {phase}")
("spec", spec), if spec:
("plan", plan), lines.append(f"spec: {spec}")
("executor", executor), if plan:
("constraints", constraints), lines.append(f"plan: {plan}")
("claimed_by", claimed_by), if executor:
("claimed_at", claimed_at), lines.append(f"executor: {executor}")
("verification", verification), if constraints:
): lines.append(f"constraints: {constraints}")
if value:
lines.append(f"{key}: {value}")
lines.append(WORKFLOW_STATE_END) lines.append(WORKFLOW_STATE_END)
return lines return lines
@ -258,7 +213,7 @@ def parse_workflow_state(
if ": " not in line: if ": " not in line:
continue continue
key, value = line.split(": ", 1) key, value = line.split(": ", 1)
if key in WORKFLOW_STATE_KEYS: if key in {"phase", "spec", "plan", "executor", "constraints"}:
state[key] = value state[key] = value
return state return state
@ -393,15 +348,9 @@ def update_workflow_state(
plan: Optional[str] = None, plan: Optional[str] = None,
executor: Optional[str] = None, executor: Optional[str] = None,
constraints: Optional[str] = None, constraints: Optional[str] = None,
claimed_by: Optional[str] = None,
claimed_at: Optional[str] = None,
verification: Optional[str] = None,
clear_keys: tuple[str, ...] = (),
) -> list[str]: ) -> list[str]:
lines, start_idx, end_idx = ensure_workflow_state_block(lines) lines, start_idx, end_idx = ensure_workflow_state_block(lines)
state = parse_workflow_state(lines, start_idx, end_idx) state = parse_workflow_state(lines, start_idx, end_idx)
for key in clear_keys:
state.pop(key, None)
if phase is not None: if phase is not None:
state["phase"] = phase state["phase"] = phase
if spec is not None: if spec is not None:
@ -412,21 +361,12 @@ def update_workflow_state(
state["executor"] = executor state["executor"] = executor
if constraints is not None: if constraints is not None:
state["constraints"] = constraints state["constraints"] = constraints
if claimed_by is not None:
state["claimed_by"] = claimed_by
if claimed_at is not None:
state["claimed_at"] = claimed_at
if verification is not None:
state["verification"] = verification
lines[start_idx : end_idx + 1] = render_workflow_state_lines( lines[start_idx : end_idx + 1] = render_workflow_state_lines(
state.get("phase"), state.get("phase"),
state.get("spec"), state.get("spec"),
state.get("plan"), state.get("plan"),
state.get("executor"), state.get("executor"),
state.get("constraints"), state.get("constraints"),
state.get("claimed_by"),
state.get("claimed_at"),
state.get("verification"),
) )
return lines return lines
@ -459,27 +399,6 @@ def filter_existing_entries(
return [entry for entry in entries if entry[0] in available] return [entry for entry in entries if entry[0] in available]
def validate_plan_meta(plan_path: Path) -> list[str]:
text = plan_path.read_text(encoding="utf-8")
missing: list[str] = []
if not re.search(r"(?im)^##\s+Plan Meta\s*$", text):
missing.append("Plan Meta")
for field in PLAN_META_REQUIRED_FIELDS:
pattern = rf"(?im)^\s*[-*]\s+(?:\*\*)?{re.escape(field)}(?:\*\*)?\s*:"
if not re.search(pattern, text):
missing.append(field)
return missing
def validate_plan_files(plans_dir: Path, plan_keys: list[str]) -> Optional[str]:
for plan_key in plan_keys:
missing = validate_plan_meta(plans_dir / plan_key)
if missing:
fields = ", ".join(missing)
return f"ERROR: {plan_key} missing required Plan Meta fields: {fields}"
return None
def choose_claim_entry( def choose_claim_entry(
entries: list[tuple[str, str, Optional[str], int]], entries: list[tuple[str, str, Optional[str], int]],
current_env: Optional[str], current_env: Optional[str],
@ -508,18 +427,13 @@ def choose_claim_entry(
return None return None
def claim_plan( def claim_plan(plans_dir: Path, progress_path: Path) -> tuple[int, str]:
plans_dir: Path, progress_path: Path, owner: Optional[str] = None
) -> tuple[int, str]:
if not plans_dir.is_dir(): if not plans_dir.is_dir():
return 2, f"ERROR: plans dir not found: {plans_dir}" return 2, f"ERROR: plans dir not found: {plans_dir}"
plan_keys = list_plan_files(plans_dir) plan_keys = list_plan_files(plans_dir)
if not plan_keys: if not plan_keys:
return 2, "ERROR: no plan files found" return 2, "ERROR: no plan files found"
plan_error = validate_plan_files(plans_dir, plan_keys)
if plan_error:
return 2, plan_error
with locked_progress(progress_path): with locked_progress(progress_path):
lines = load_progress_lines(progress_path) lines = load_progress_lines(progress_path)
@ -544,9 +458,6 @@ def claim_plan(
lines, lines,
phase="executing", phase="executing",
plan=(plans_dir / plan_key).as_posix(), plan=(plans_dir / plan_key).as_posix(),
claimed_by=normalize_note(owner) if owner else default_claim_owner(),
claimed_at=now_utc(),
clear_keys=("verification",),
) )
write_progress_lines(progress_path, lines) write_progress_lines(progress_path, lines)
@ -557,18 +468,12 @@ def claim_plan(
def finish_plan( def finish_plan(
plan: str, plan: str, status: str, progress_path: Path, note: Optional[str]
status: str,
progress_path: Path,
note: Optional[str],
verified: Optional[str] = None,
) -> tuple[int, str]: ) -> tuple[int, str]:
if status not in FINISH_STATUSES: if status not in FINISH_STATUSES:
return 2, f"ERROR: invalid status: {status}" return 2, f"ERROR: invalid status: {status}"
if not plan: if not plan:
return 2, "ERROR: plan is required" return 2, "ERROR: plan is required"
if verified and status != "done":
return 2, "ERROR: -verified is only valid with -status done"
plan_key = normalize_plan_key(plan) plan_key = normalize_plan_key(plan)
with locked_progress(progress_path): with locked_progress(progress_path):
@ -583,15 +488,6 @@ def finish_plan(
entries = parse_entries(lines, start_idx, end_idx) entries = parse_entries(lines, start_idx, end_idx)
rendered_note = normalize_note(note) if note else None rendered_note = normalize_note(note) if note else None
rendered_verified = normalize_note(verified) if verified else None
verification_clear_keys = () if rendered_verified else ("verification",)
if rendered_verified:
verified_note = f"verified: {rendered_verified}"
rendered_note = (
f"{verified_note}; note: {rendered_note}"
if rendered_note
else verified_note
)
updated_line = render_plan_line(plan_key, status, rendered_note) updated_line = render_plan_line(plan_key, status, rendered_note)
workflow_phase = { workflow_phase = {
"done": "done", "done": "done",
@ -606,8 +502,6 @@ def finish_plan(
lines, lines,
phase=workflow_phase, phase=workflow_phase,
plan=f"docs/superpowers/plans/{plan_key}", plan=f"docs/superpowers/plans/{plan_key}",
verification=rendered_verified,
clear_keys=verification_clear_keys,
) )
write_progress_lines(progress_path, lines) write_progress_lines(progress_path, lines)
return 0, updated_line return 0, updated_line
@ -617,64 +511,11 @@ def finish_plan(
lines, lines,
phase=workflow_phase, phase=workflow_phase,
plan=f"docs/superpowers/plans/{plan_key}", plan=f"docs/superpowers/plans/{plan_key}",
verification=rendered_verified,
clear_keys=verification_clear_keys,
) )
write_progress_lines(progress_path, lines) write_progress_lines(progress_path, lines)
return 0, updated_line return 0, updated_line
def status_report(plans_dir: Path, progress_path: Path) -> tuple[int, str]:
if not plans_dir.is_dir():
return 2, f"ERROR: plans dir not found: {plans_dir}"
plan_keys = list_plan_files(plans_dir)
lines = load_progress_lines(progress_path)
block = find_block(lines)
entries: list[tuple[str, str, Optional[str], int]] = []
if block:
entries = filter_existing_entries(parse_entries(lines, *block), plan_keys)
entry_by_plan = {plan_key: (status, note) for plan_key, status, note, _ in entries}
counts = {status: 0 for status in ("pending", "in-progress", "done", "blocked", "skipped")}
rows: list[str] = []
for plan_key in plan_keys:
status, note = entry_by_plan.get(plan_key, ("pending", None))
counts[status] += 1
suffix = f": {note}" if note else ""
rows.append(f"PLAN {plan_key} {status}{suffix}")
state: dict[str, str] = {}
workflow_block = find_named_block(lines, WORKFLOW_STATE_START, WORKFLOW_STATE_END)
if workflow_block:
state = parse_workflow_state(lines, *workflow_block)
output = [
"STATUS "
f"total={len(plan_keys)} "
f"pending={counts['pending']} "
f"in-progress={counts['in-progress']} "
f"done={counts['done']} "
f"blocked={counts['blocked']} "
f"skipped={counts['skipped']}"
]
current_parts = [
f"{key}={state[key]}"
for key in (
"phase",
"plan",
"claimed_by",
"claimed_at",
"verification",
)
if key in state
]
if current_parts:
output.append("CURRENT " + " ".join(current_parts))
output.extend(rows)
return 0, "\n".join(output)
def record_workflow_state( def record_workflow_state(
progress_path: Path, progress_path: Path,
phase: str, phase: str,
@ -702,7 +543,7 @@ def main(argv: list[str]) -> int:
return 0 return 0
mode = argv[0] mode = argv[0]
if mode not in {"claim", "finish", "record", "status"}: if mode not in {"claim", "finish", "record"}:
print(f"ERROR: unknown mode: {mode}", file=sys.stderr) print(f"ERROR: unknown mode: {mode}", file=sys.stderr)
print(usage(), file=sys.stderr) print(usage(), file=sys.stderr)
return 2 return 2
@ -720,26 +561,11 @@ def main(argv: list[str]) -> int:
if mode == "claim": if mode == "claim":
plans = flags.get("-plans") plans = flags.get("-plans")
progress = flags.get("-progress") progress = flags.get("-progress")
owner = flags.get("-owner")
if not plans or not progress: if not plans or not progress:
print("ERROR: -plans and -progress are required", file=sys.stderr) print("ERROR: -plans and -progress are required", file=sys.stderr)
print(usage(), file=sys.stderr) print(usage(), file=sys.stderr)
return 2 return 2
code, message = claim_plan(Path(plans), Path(progress), owner) code, message = claim_plan(Path(plans), Path(progress))
if code != 0:
print(message, file=sys.stderr)
return code
print(message)
return 0
if mode == "status":
plans = flags.get("-plans")
progress = flags.get("-progress")
if not plans or not progress:
print("ERROR: -plans and -progress are required", file=sys.stderr)
print(usage(), file=sys.stderr)
return 2
code, message = status_report(Path(plans), Path(progress))
if code != 0: if code != 0:
print(message, file=sys.stderr) print(message, file=sys.stderr)
return code return code
@ -770,12 +596,11 @@ def main(argv: list[str]) -> int:
status = flags.get("-status") status = flags.get("-status")
progress = flags.get("-progress") progress = flags.get("-progress")
note = flags.get("-note") note = flags.get("-note")
verified = flags.get("-verified")
if not plan or not status or not progress: if not plan or not status or not progress:
print("ERROR: -plan, -status, and -progress are required", file=sys.stderr) print("ERROR: -plan, -status, and -progress are required", file=sys.stderr)
print(usage(), file=sys.stderr) print(usage(), file=sys.stderr)
return 2 return 2
code, message = finish_plan(plan, status, Path(progress), note, verified) code, message = finish_plan(plan, status, Path(progress), note)
if code != 0: if code != 0:
print(message, file=sys.stderr) print(message, file=sys.stderr)
return code return code

View File

@ -1,72 +0,0 @@
# Skills Overview
本目录放置 Playbook 可安装的 AI agent skills。
- 一方 skill 位于 `skills/<name>/SKILL.md`,由本仓库维护。
- 第三方 skill 位于 `skills/thirdparty/<name>/SKILL.md`,由同步流程生成或更新。
- 第三方来源登记见 `skills/thirdparty/thirdparty-skills.yml`
`.gitea/ci/thirdparty_skills.json`
## 一方 Skills
| Skill | 作用 | 典型场景 |
| --- | --- | --- |
| `commit-message` | 根据 staged diff 生成符合仓库规范的提交信息,并判断是否应拆分提交 | 写 commit message、检查 staged 改动是否适合一个提交 |
| `gitea-fix-ci` | 基于 Gitea Actions run/job/log 诊断失败 CI先形成修复计划再改代码 | Gitea PR checks 失败、远端 CI 红但本地需要定位 |
| `style-cleanup` | 使用仓库既有 formatter/linter 做格式和 lint 收尾,不改变语义 | 格式化、lint cleanup、代码改完后的风格整理 |
## 已同步第三方 Skills
### Superpowers Workflow Suite
这些 skill 来自 `superpowers`,主要提供计划、执行、调试、验证和审查工作流。
| Skill | 作用 |
| --- | --- |
| `using-superpowers` | 会话开始时判断并加载适用 skill |
| `brainstorming` | 需求澄清、方案讨论、设计文档产出 |
| `writing-plans` | 把设计或需求拆成可执行 implementation plan |
| `executing-plans` | 按已写好的 plan 执行任务 |
| `subagent-driven-development` | 用子 agent 按任务执行、审查和迭代 plan |
| `dispatching-parallel-agents` | 多个互不依赖问题并行分派 |
| `using-git-worktrees` | 需要隔离工作区时创建或使用 git worktree |
| `finishing-a-development-branch` | 开发分支完成后的合并、PR、保留或丢弃流程 |
| `test-driven-development` | 新功能、bugfix、重构前的 TDD 流程 |
| `systematic-debugging` | bug、测试失败、异常行为的根因定位流程 |
| `verification-before-completion` | 声称完成、修复或通过前的验证门禁 |
| `requesting-code-review` | 主动请求代码审查,通常用于任务完成或合并前 |
| `receiving-code-review` | 处理别人给出的 review 意见,判断采纳、反驳或澄清 |
| `writing-skills` | 创建、修改或验证 skill 的写作规范 |
### Other Synced Third-party Skills
| Skill | 来源 | 作用 |
| --- | --- | --- |
| `ui-ux-pro-max` | `ui-ux-pro-max-skill` | UI/UX 设计知识库,包含风格、配色、字体、组件和栈相关建议 |
| `karpathy-guidelines` | `andrej-karpathy-skills` | 写代码、审查、重构时降低 LLM 常见错误:保持简单、聚焦、可验证 |
## 已登记待同步 Third-party Skills
这些来源已登记,后续同步后会落到 `skills/thirdparty/`
### brooks-lint Suite
`brooks-lint` 是一个套件来源,不是单个 skill。同步时会包含多个下游 skill
以及共享资料目录:
| 下游项 | 隶属 | 作用 |
| --- | --- | --- |
| `brooks-review` | `brooks-lint` | 常规 PR / diff code review |
| `brooks-audit` | `brooks-lint` | architecture audit、系统性结构问题审查 |
| `brooks-debt` | `brooks-lint` | technical debt review |
| `brooks-health` | `brooks-lint` | 项目健康度检查 |
| `brooks-sweep` | `brooks-lint` | 扫描式问题发现 |
| `brooks-test` | `brooks-lint` | 测试质量审查 |
| `_shared` | `brooks-lint` | brooks 系列 skill 共享参考资料,不是独立使用的 skill |
### Single-skill Sources
| Skill | 来源 | 作用 |
| --- | --- | --- |
| `codebase-recon` | `outfitter-dev/agents` | 通过 git 历史和代码结构做 risk scan、热点分析、重构前侦察 |
| `codebase-migrate` | `awesome-codex-skills` | 大代码库迁移、多文件 refactor、分批变更与 CI 验证工作流 |

View File

@ -0,0 +1,109 @@
---
name: bulk-refactor-workflow
description: Use when renaming symbols, migrating APIs, or applying safe mechanical changes across many files in the repository.
---
# Bulk Refactor Workflow批量重构 / 大范围修改)
## Overview
Guide repository-wide mechanical edits without losing scope control or damaging
unrelated work. Core principle: enumerate first, transform mechanically, verify
in loops, and keep every batch auditable.
## When to Use
- Rename APIs, symbols, file conventions, or repeated string patterns
- Apply scripted or structural edits across many files
- Migrate callers to a new interface where the change pattern is repeatable
## When Not to Use
- One-off logic changes where no repeatable transformation exists
- Exploratory redesign or feature work that needs new architecture decisions
- Cleanup that is only formatting/lint alignment; use `style-cleanup` directly
## Inputs
- Scope: directories, file types, include/exclude rules
- Transformation: rename, replace, codemod, scripted edit, migration strategy
- Constraints: behavior-preserving vs. compatibility window vs. staged rollout
- Verification commands: smallest useful loop plus final gate
## Procedure
1. **Baseline and bound the scope**
- Record `git status --short`.
- Dirty worktrees are allowed.
- Do not revert unrelated changes.
- Mark the exact target file set before editing.
- Run a baseline verification command so existing failures are not misattributed
to the refactor.
2. **Enumerate every intended hit**
- Search before editing with `rg`, `git grep`, or a structured query.
- Separate real code hits from comments, docs, generated files, and samples.
- Write down exclusions before running the transformation.
3. **Choose the safest mechanical transform**
- Prefer deterministic transforms: codemods, scripts, structured edits, or
narrow search/replace with explicit scope.
- If compatibility matters, use a staged migration: adapt providers first,
migrate callers second, remove compatibility layer last.
4. **Apply in bounded batches**
- First, apply the transformation in bounded batches.
- Verify each batch before widening scope.
- Stop immediately if output differs from the expected hit class.
5. **Verify in loops**
- Run the smallest relevant verification after each batch.
- Re-check the hit list after edits to confirm the intended pattern is gone and
unintended patterns were not introduced.
6. **Finish with targeted cleanup**
- If the mechanical change leaves formatting or lint fallout, Use `style-cleanup` for the final formatting/lint pass.
- Keep that cleanup scoped to the refactor set unless the user approves a wider
pass.
7. **Report**
- Summarize files touched, transform rules used, verification evidence, and any
residual risk.
## Output Contract
- `Scope:` files, directories, and exclusions actually used
- `Transformation:` exact rule or script applied
- `Batches:` how the work was partitioned
- `Verification:` commands, exit status, and what each loop proved
- `Risks:` remaining ambiguous areas, compatibility debt, or deferred cleanup
## Success Criteria
- Every changed file belongs to the declared scope
- The transformation is reproducible and explainable
- Verification passes at both batch level and final gate
- Unrelated work in the repository is preserved
## Failure Handling
- If the hit list is ambiguous, stop and refine scope before editing
- If the transform cannot be expressed mechanically, downgrade to a normal
refactor plan instead of pretending it is safe bulk work
- If verification fails mid-batch, stop, inspect the smallest failing delta, and
correct the transform before continuing
- If compatibility risk is higher than expected, split the migration into staged
commits or phases
## Guardrails
- Never run repository-wide replacement without an explicit hit review
- Never mix unrelated cleanup into the same batch
- Never hide a large-scope behavior change behind the label "mechanical refactor"

View File

@ -1,148 +0,0 @@
---
name: gitea-fix-ci
description: Use when a user asks to debug or fix failing Gitea Actions, Gitea PR checks, or CI workflow runs for a Gitea-hosted repository.
---
# Gitea Fix CI
## Overview
Diagnose failing Gitea Actions from the pull request or workflow run, extract the
smallest useful failure context, then propose a fix plan before changing code.
Core principle: CI logs are evidence; do not guess from the red status alone.
This is not a standalone executor. It guides use of `tea`, local `git`, and the
Gitea API from the current workspace.
## When to Use
- A Gitea-hosted repository has failing Gitea Actions or PR checks
- The user asks to inspect a failed workflow run, job, or CI status
- The user asks to fix CI after a push, branch update, or pull request update
- Local tests pass but remote Gitea Actions fail
## When Not to Use
- The repository is not hosted on Gitea or Forgejo-compatible infrastructure
- The failure belongs to an external CI provider and only links out from Gitea
- The user only wants a local test or lint run
- Credentials, tokens, or network access are unavailable and the user has not
provided the failing log text
## Inputs
- Repository path, defaulting to the current workspace
- Gitea base URL and repository owner/name, from `git remote -v` when possible
- Pull request number, branch, commit SHA, or workflow run ID
- Authentication method: `tea` login profile, `GITEA_TOKEN` for API requests,
or an existing git credential for the Gitea web fallback
- Any pasted CI log if remote access is unavailable
## Procedure
1. **Baseline local state**
- Record `git status --short`, current branch, and latest commit SHA.
- Identify the Gitea remote URL and owner/repo.
- Do not modify files while gathering CI evidence.
2. **Verify Gitea access**
- Prefer `tea` if it is installed and authenticated:
- `tea login list`
- `tea actions runs list --status failure --branch <branch>`
- `tea actions runs view <run-id>`
- `tea actions runs logs <run-id> --job <job-id>`
- `tea pulls view <pr>`
- If `tea` is unavailable or lacks an Actions command for the installed
version, use the Gitea API directly.
- Check `/api/v1/version` and `/swagger.v1.json` when API behavior is
unclear. Gitea 1.21 exposes Actions pages but not workflow run/job/log API
endpoints.
- Never print tokens. Pass API tokens through environment variables.
3. **Find failing workflow runs**
- For a known run ID, fetch that run directly.
- Otherwise list recent workflow runs filtered by branch, event, status, or
commit SHA.
- API patterns:
- `GET /api/v1/repos/<owner>/<repo>/actions/runs`
- `GET /api/v1/repos/<owner>/<repo>/actions/runs/<run>`
- `GET /api/v1/repos/<owner>/<repo>/actions/runs/<run>/jobs`
- Web fallback for older Gitea:
- `GET /<owner>/<repo>/actions`
- Parse `/<owner>/<repo>/actions/runs/<run>` links and status labels.
- Treat `failure`, `cancelled`, and missing required checks differently.
Cancelled jobs may require rerun or queue investigation rather than code
changes.
4. **Fetch job logs**
- For each failed job, download the job logs:
- `GET /api/v1/repos/<owner>/<repo>/actions/jobs/<job_id>/logs`
- On Gitea 1.21 web fallback, download logs by UI job index:
- `GET /<owner>/<repo>/actions/runs/<run>/jobs/<job-index>/logs`
- Start with job index `0`; if multiple jobs exist, inspect the run page or
downloaded logs to map the failing job.
- Save large logs to a temporary file outside tracked source paths.
- Extract the first actionable error block, surrounding command, job name,
workflow name, run URL, branch, and SHA.
- If logs are missing, report that explicitly instead of inventing causes.
5. **Classify the failure**
- Code/test failure: failing assertion, compile error, lint error, type error
- Environment failure: missing secret, runner image, dependency install,
network, cache, permission, or service startup
- Workflow failure: invalid YAML, unsupported syntax, wrong trigger, bad path,
wrong branch/ref assumption
- Infrastructure failure: offline runner, stuck queue, cancelled run, timeout
6. **Create a fix plan**
- Summarize the failure evidence with exact job/run identifiers.
- Propose the smallest code or workflow change that matches the evidence.
- Include local verification commands and the remote recheck path.
- Do not implement before the user approves the fix plan.
7. **Implement after approval**
- Apply only the approved fix.
- Run the local command that most closely reproduces the failed job.
- If the failure is workflow-only, validate the workflow file syntax and any
referenced paths or scripts.
8. **Recheck**
- Tell the user what must be pushed or rerun in Gitea.
- If permitted, use the Gitea API to inspect the rerun status.
- Final output must distinguish local verification from remote CI status.
## Output Contract
- `Target:` repo, branch/SHA, PR or run ID
- `Failed CI:` workflow, job, status, run URL or API path
- `Evidence:` concise log snippet and classification
- `Plan:` proposed fix, local verification, remote recheck
- `Changes:` files changed after approval
- `Result:` local checks run and remaining remote status
## Success Criteria
- Failure analysis is based on Gitea Actions run/job data or pasted logs
- The fix plan names the exact workflow run or job it addresses
- No code or workflow edits happen before plan approval
- Verification distinguishes local commands from remote Gitea Actions results
- Tokens and private log content are not echoed unnecessarily
## Failure Handling
- If authentication fails, ask the user to authenticate `tea` or provide a token
through the environment; do not request secrets in chat
- If the Gitea version lacks Actions API endpoints, ask for the relevant log text
or a browser-copied job log
- If an external CI provider owns the failing check, report the external URL and
stop at evidence collection
- If the failure is infrastructure-only, recommend rerun/runner investigation
instead of editing code

View File

@ -1,55 +0,0 @@
# Third-party skill registry.
version: 1
skills:
- id: brooks-lint
sync: enabled
upstream_repo: https://github.com/hyhmrright/brooks-lint
upstream_ref: main
upstream_layout: multi_skill_suite
upstream_paths:
- skills/brooks-audit
- skills/brooks-debt
- skills/brooks-health
- skills/brooks-review
- skills/brooks-sweep
- skills/brooks-test
- skills/_shared
selected_for:
- architecture audit
- technical debt review
- PR review
- test quality review
playbook_fit: architecture and code-quality review support
notes:
- Upstream is a suite of related review and audit skills.
- id: codebase-recon
sync: enabled
upstream_repo: https://github.com/outfitter-dev/agents
upstream_ref: main
upstream_path: plugins/outfitter/skills/codebase-recon
upstream_layout: single_skill_dir
selected_for:
- risk scan
- git-history hotspot analysis
- codebase reconnaissance before planning
- refactor-risk assessment
playbook_fit: pre-design and pre-refactor codebase risk discovery
notes:
- Upstream skill lives inside a larger agents repository.
- id: codebase-migrate
sync: enabled
upstream_repo: https://github.com/ComposioHQ/awesome-codex-skills
upstream_ref: main
upstream_path: codebase-migrate
upstream_layout: single_skill_dir
selected_for:
- large codebase migrations
- multi-file refactors
- reviewable refactor batches
- CI-verified migration workflows
notes:
- Sourced from awesome-codex-skills.

View File

@ -86,22 +86,6 @@
再更新 `progress.md` 上半部分摘要,并按主循环收尾要求归档当前 再更新 `progress.md` 上半部分摘要,并按主循环收尾要求归档当前
Plan 变更 Plan 变更
### 条件触发 Skills
以下 skill 是额外能力,只在满足触发条件时使用,不改变主流程顺序,
也不由 `main_loop.py` 自动调度:
| Skill | 触发条件 | 负责范围 |
| ------------------ | -------------------------------------- | -------------------------------------- |
| `codebase-recon` | 架构、跨模块、重构、迁移或风险不明任务 | 代码库侦察、热点分析、影响面判断 |
| `brooks-audit` | 方案影响架构边界、模块职责或长期维护性 | 架构审查、结构风险识别 |
| `codebase-migrate` | 大规模迁移、多文件重构、API 替换 | 分批迁移、可审查 refactor、CI 验证节奏 |
| `brooks-review` | 代码类 Plan 完成后,归档前需要审查 | diff / PR 级代码审查 |
| `brooks-test` | 测试改动复杂,或需要确认测试质量 | 测试有效性、覆盖边界、断言质量审查 |
| `gitea-fix-ci` | Gitea Actions 失败 | 拉取 CI 日志、定位失败、形成修复计划 |
| `style-cleanup` | 代码实现后需要格式或 lint 收尾 | 格式化、lint cleanup不改变语义 |
| `commit-message` | 需要提交或归档当前 Plan 改动 | commit message 生成与 staged diff 检查 |
### Plan 要求 ### Plan 要求
- `Plan Meta` 必填,位于 Plan 头部 `---` 之后、Task 1 之前 - `Plan Meta` 必填,位于 Plan 头部 `---` 之后、Task 1 之前
@ -151,20 +135,16 @@
```bash ```bash
python {{PLAYBOOK_SCRIPTS}}/main_loop.py claim \ python {{PLAYBOOK_SCRIPTS}}/main_loop.py claim \
-plans docs/superpowers/plans \ -plans docs/superpowers/plans \
-progress memory-bank/progress.md \ -progress memory-bank/progress.md
-owner "<当前session或agent标识>"
``` ```
该命令会在锁保护下串行完成三件事: 该命令会在锁保护下串行完成三件事:
- 自动识别当前环境:`windows`、`linux`、`darwin` - 自动识别当前环境:`windows`、`linux`、`darwin`
- 校验 Plan 文件包含必需 `Plan Meta`
- 已有 `in-progress` 优先恢复 - 已有 `in-progress` 优先恢复
- 如无 `in-progress`,按 Plan 文件顺序选择第一个可执行 Plan - 如无 `in-progress`,按 Plan 文件顺序选择第一个可执行 Plan
`pending``blocked: env:<当前环境>:...` `pending``blocked: env:<当前环境>:...`
- 将选中的 Plan 写成 `in-progress` - 将选中的 Plan 写成 `in-progress`
- 在 `workflow-state` 写入 `claimed_by`、`claimed_at`,并清理上一轮
`verification`
这里的锁保护的是 `progress.md` 状态块更新,避免多个 session 这里的锁保护的是 `progress.md` 状态块更新,避免多个 session
同时读写时发生覆盖。 同时读写时发生覆盖。
@ -196,8 +176,7 @@ python {{PLAYBOOK_SCRIPTS}}/playbook.py \
python {{PLAYBOOK_SCRIPTS}}/main_loop.py finish \ python {{PLAYBOOK_SCRIPTS}}/main_loop.py finish \
-plan <plan> \ -plan <plan> \
-status done \ -status done \
-progress memory-bank/progress.md \ -progress memory-bank/progress.md
-verified "<本轮已通过的验证命令或证据>"
``` ```
```bash ```bash
@ -216,17 +195,6 @@ python {{PLAYBOOK_SCRIPTS}}/main_loop.py finish \
-note "<原因>" -note "<原因>"
``` ```
只读状态命令:
```bash
python {{PLAYBOOK_SCRIPTS}}/main_loop.py status \
-plans docs/superpowers/plans \
-progress memory-bank/progress.md
```
`status` 只汇总 `pending`、`in-progress`、`done`、`blocked`、
`skipped` 和当前 `workflow-state`,不得修改 `progress.md`
### Plan 场景下的执行上下文与隔离策略 ### Plan 场景下的执行上下文与隔离策略
- 本节仅适用于通过主循环领取并执行 Plan 的场景 - 本节仅适用于通过主循环领取并执行 Plan 的场景
@ -267,14 +235,11 @@ python {{PLAYBOOK_SCRIPTS}}/main_loop.py status \
- Plan `done` 后必须完成当前 Plan 变更的归档/提交,然后才能继续领取下一个 - Plan `done` 后必须完成当前 Plan 变更的归档/提交,然后才能继续领取下一个
Plan归档方式由项目约定决定 Plan归档方式由项目约定决定
- 收尾顺序: - 收尾顺序:
1. 完成 Plan 约定验证
1. 完成 Plan 约定验证 2. 运行 `main_loop.py finish -status done` 写回状态
2. 运行 `main_loop.py finish -status done -verified "<证据>"` 3. 必要时更新 `progress.md` 上半部分摘要和相关 memory
写回状态 4. 检查当前变更清单与差异
3. 必要时更新 `progress.md` 上半部分摘要和相关 memory 5. 只归档/提交当前 Plan 相关改动
4. 检查当前变更清单与差异
5. 只归档/提交当前 Plan 相关改动
- 当前 Plan 相关改动包括但不限于: - 当前 Plan 相关改动包括但不限于:
- 本轮代码、配置、测试、模板改动 - 本轮代码、配置、测试、模板改动
- 当前 Plan 文件(创建、补充、勾选 Task、记录结果等 - 当前 Plan 文件(创建、补充、勾选 Task、记录结果等

View File

@ -7,8 +7,8 @@ ROOT = Path(__file__).resolve().parents[1]
SKILLS_ROOT = ROOT / "skills" SKILLS_ROOT = ROOT / "skills"
FIRST_PARTY_SKILLS = { FIRST_PARTY_SKILLS = {
"commit-message": SKILLS_ROOT / "commit-message" / "SKILL.md", "commit-message": SKILLS_ROOT / "commit-message" / "SKILL.md",
"gitea-fix-ci": SKILLS_ROOT / "gitea-fix-ci" / "SKILL.md",
"style-cleanup": SKILLS_ROOT / "style-cleanup" / "SKILL.md", "style-cleanup": SKILLS_ROOT / "style-cleanup" / "SKILL.md",
"bulk-refactor-workflow": SKILLS_ROOT / "bulk-refactor-workflow" / "SKILL.md",
} }
@ -92,20 +92,13 @@ class FirstPartySkillsQualityTests(unittest.TestCase):
self.assertIn("formatter -> lint/check -> lint --fix -> final check", text) self.assertIn("formatter -> lint/check -> lint --fix -> final check", text)
self.assertIn("second formatter run produces no additional diff", text) self.assertIn("second formatter run produces no additional diff", text)
def test_gitea_fix_ci_skill_is_gitea_specific_and_plan_gated(self): def test_bulk_refactor_skill_is_dirty_aware_and_delegates_final_cleanup(self):
text = normalize_space(read_text(FIRST_PARTY_SKILLS["gitea-fix-ci"])) text = normalize_space(read_text(FIRST_PARTY_SKILLS["bulk-refactor-workflow"]))
self.assertIn("Gitea Actions", text) self.assertIn("Dirty worktrees are allowed", text)
self.assertIn("tea", text) self.assertIn("Do not revert unrelated changes", text)
self.assertIn("tea actions runs", text) self.assertIn("Use `style-cleanup` for the final formatting/lint pass", text)
self.assertIn("Gitea API", text) self.assertIn("apply the transformation in bounded batches", text)
self.assertIn("Gitea 1.21", text)
self.assertIn("workflow runs", text)
self.assertIn("job logs", text)
self.assertIn("/actions/runs/<run>/jobs/<job-index>/logs", text)
self.assertIn("Do not implement before the user approves the fix plan", text)
self.assertIn("not a standalone executor", text)
self.assertNotIn("gh pr checks", text)
self.assertNotIn("GitHub Actions", text)
if __name__ == "__main__": if __name__ == "__main__":
unittest.main() unittest.main()

View File

@ -27,26 +27,6 @@ def run_cli(*args, cwd=None):
) )
def valid_plan_text(title: str = "Demo Plan") -> str:
return "\n".join(
[
f"# {title}",
"",
"## Plan Meta",
"",
"- **Plan Group**: `demo`",
"- **Parent Plan**: `none`",
"- **Verification Scope**: `unit`",
"- **Verification Gate**: `python -m unittest tests.test_main_loop_cli`",
"",
"## Tasks",
"",
"- [ ] Task 1: demo",
"",
]
)
class MainLoopCliTests(unittest.TestCase): class MainLoopCliTests(unittest.TestCase):
def _current_env(self) -> str: def _current_env(self) -> str:
system = platform.system().lower() system = platform.system().lower()
@ -60,12 +40,8 @@ class MainLoopCliTests(unittest.TestCase):
root = Path(tmp_dir) root = Path(tmp_dir)
plans_dir = root / "docs" / "superpowers" / "plans" plans_dir = root / "docs" / "superpowers" / "plans"
plans_dir.mkdir(parents=True) plans_dir.mkdir(parents=True)
(plans_dir / "2026-01-01-old.md").write_text( (plans_dir / "2026-01-01-old.md").write_text("old", encoding="utf-8")
valid_plan_text("old"), encoding="utf-8" (plans_dir / "2026-01-02-new.md").write_text("new", encoding="utf-8")
)
(plans_dir / "2026-01-02-new.md").write_text(
valid_plan_text("new"), encoding="utf-8"
)
result = run_cli( result = run_cli(
"claim", "claim",
@ -98,9 +74,7 @@ class MainLoopCliTests(unittest.TestCase):
root = Path(tmp_dir) root = Path(tmp_dir)
plans_dir = root / "docs" / "superpowers" / "plans" plans_dir = root / "docs" / "superpowers" / "plans"
plans_dir.mkdir(parents=True) plans_dir.mkdir(parents=True)
(plans_dir / "2026-01-01-demo.md").write_text( (plans_dir / "2026-01-01-demo.md").write_text("demo", encoding="utf-8")
valid_plan_text(), encoding="utf-8"
)
progress = root / "memory-bank" / "progress.md" progress = root / "memory-bank" / "progress.md"
progress.parent.mkdir(parents=True) progress.parent.mkdir(parents=True)
@ -145,12 +119,8 @@ class MainLoopCliTests(unittest.TestCase):
root = Path(tmp_dir) root = Path(tmp_dir)
plans_dir = root / "docs" / "superpowers" / "plans" plans_dir = root / "docs" / "superpowers" / "plans"
plans_dir.mkdir(parents=True) plans_dir.mkdir(parents=True)
(plans_dir / "2026-01-01-a.md").write_text( (plans_dir / "2026-01-01-a.md").write_text("a", encoding="utf-8")
valid_plan_text("a"), encoding="utf-8" (plans_dir / "2026-01-02-b.md").write_text("b", encoding="utf-8")
)
(plans_dir / "2026-01-02-b.md").write_text(
valid_plan_text("b"), encoding="utf-8"
)
progress = root / "memory-bank" / "progress.md" progress = root / "memory-bank" / "progress.md"
progress.parent.mkdir(parents=True) progress.parent.mkdir(parents=True)
@ -189,9 +159,7 @@ class MainLoopCliTests(unittest.TestCase):
root = Path(tmp_dir) root = Path(tmp_dir)
plans_dir = root / "docs" / "superpowers" / "plans" plans_dir = root / "docs" / "superpowers" / "plans"
plans_dir.mkdir(parents=True) plans_dir.mkdir(parents=True)
(plans_dir / "2026-01-02-live.md").write_text( (plans_dir / "2026-01-02-live.md").write_text("live", encoding="utf-8")
valid_plan_text("live"), encoding="utf-8"
)
progress = root / "memory-bank" / "progress.md" progress = root / "memory-bank" / "progress.md"
progress.parent.mkdir(parents=True) progress.parent.mkdir(parents=True)
@ -234,9 +202,7 @@ class MainLoopCliTests(unittest.TestCase):
root = Path(tmp_dir) root = Path(tmp_dir)
plans_dir = root / "docs" / "superpowers" / "plans" plans_dir = root / "docs" / "superpowers" / "plans"
plans_dir.mkdir(parents=True) plans_dir.mkdir(parents=True)
(plans_dir / "2026-01-05-env.md").write_text( (plans_dir / "2026-01-05-env.md").write_text("env", encoding="utf-8")
valid_plan_text("env"), encoding="utf-8"
)
progress = root / "memory-bank" / "progress.md" progress = root / "memory-bank" / "progress.md"
progress.parent.mkdir(parents=True) progress.parent.mkdir(parents=True)
@ -284,11 +250,9 @@ class MainLoopCliTests(unittest.TestCase):
root = Path(tmp_dir) root = Path(tmp_dir)
plans_dir = root / "docs" / "superpowers" / "plans" plans_dir = root / "docs" / "superpowers" / "plans"
plans_dir.mkdir(parents=True) plans_dir.mkdir(parents=True)
(plans_dir / "2026-01-01-env.md").write_text( (plans_dir / "2026-01-01-env.md").write_text("env", encoding="utf-8")
valid_plan_text("env"), encoding="utf-8"
)
(plans_dir / "2026-01-02-pending.md").write_text( (plans_dir / "2026-01-02-pending.md").write_text(
valid_plan_text("pending"), encoding="utf-8" "pending", encoding="utf-8"
) )
progress = root / "memory-bank" / "progress.md" progress = root / "memory-bank" / "progress.md"
@ -330,99 +294,6 @@ class MainLoopCliTests(unittest.TestCase):
), ),
) )
def test_claim_rejects_plan_missing_required_plan_meta(self):
with tempfile.TemporaryDirectory() as tmp_dir:
root = Path(tmp_dir)
plans_dir = root / "docs" / "superpowers" / "plans"
plans_dir.mkdir(parents=True)
(plans_dir / "2026-01-01-invalid.md").write_text(
"# Invalid Plan\n\n- [ ] Task 1: missing metadata\n",
encoding="utf-8",
)
result = run_cli(
"claim",
"-plans",
"docs/superpowers/plans",
"-progress",
"memory-bank/progress.md",
cwd=root,
)
self.assertEqual(result.returncode, 2)
self.assertIn("missing required Plan Meta", result.stderr)
self.assertIn("2026-01-01-invalid.md", result.stderr)
def test_claim_records_claim_metadata_in_workflow_state(self):
with tempfile.TemporaryDirectory() as tmp_dir:
root = Path(tmp_dir)
plans_dir = root / "docs" / "superpowers" / "plans"
plans_dir.mkdir(parents=True)
(plans_dir / "2026-01-01-demo.md").write_text(
valid_plan_text(), encoding="utf-8"
)
result = run_cli(
"claim",
"-plans",
"docs/superpowers/plans",
"-progress",
"memory-bank/progress.md",
"-owner",
"codex-test",
cwd=root,
)
self.assertEqual(result.returncode, 0, msg=result.stderr)
text = (root / "memory-bank" / "progress.md").read_text(
encoding="utf-8"
)
self.assertIn("claimed_by: codex-test", text)
self.assertRegex(text, r"claimed_at: \d{4}-\d{2}-\d{2}T")
def test_claim_clears_stale_verification_from_workflow_state(self):
with tempfile.TemporaryDirectory() as tmp_dir:
root = Path(tmp_dir)
plans_dir = root / "docs" / "superpowers" / "plans"
plans_dir.mkdir(parents=True)
(plans_dir / "2026-01-01-demo.md").write_text(
valid_plan_text(), encoding="utf-8"
)
progress = root / "memory-bank" / "progress.md"
progress.parent.mkdir(parents=True)
progress.write_text(
"\n".join(
[
"# 当前进展",
"",
"<!-- workflow-state:start -->",
"phase: done",
"verification: old evidence",
"<!-- workflow-state:end -->",
"",
"<!-- plan-status:start -->",
"- [ ] `2026-01-01-demo.md` pending",
"<!-- plan-status:end -->",
"",
]
),
encoding="utf-8",
)
result = run_cli(
"claim",
"-plans",
"docs/superpowers/plans",
"-progress",
"memory-bank/progress.md",
cwd=root,
)
self.assertEqual(result.returncode, 0, msg=result.stderr)
text = progress.read_text(encoding="utf-8")
self.assertNotIn("verification: old evidence", text)
def test_finish_updates_line(self): def test_finish_updates_line(self):
with tempfile.TemporaryDirectory() as tmp_dir: with tempfile.TemporaryDirectory() as tmp_dir:
root = Path(tmp_dir) root = Path(tmp_dir)
@ -461,98 +332,6 @@ class MainLoopCliTests(unittest.TestCase):
1, 1,
) )
def test_finish_done_records_verification_evidence(self):
with tempfile.TemporaryDirectory() as tmp_dir:
root = Path(tmp_dir)
progress = root / "memory-bank" / "progress.md"
progress.parent.mkdir(parents=True)
progress.write_text(
"\n".join(
[
"# Plan 状态",
"",
"<!-- workflow-state:start -->",
"phase: executing",
"plan: docs/superpowers/plans/2026-01-03-demo.md",
"<!-- workflow-state:end -->",
"",
"<!-- plan-status:start -->",
"- [ ] `2026-01-03-demo.md` in-progress",
"<!-- plan-status:end -->",
"",
]
),
encoding="utf-8",
)
result = run_cli(
"finish",
"-plan",
"docs/superpowers/plans/2026-01-03-demo.md",
"-status",
"done",
"-progress",
"memory-bank/progress.md",
"-verified",
"python -m unittest tests.test_main_loop_cli",
cwd=root,
)
self.assertEqual(result.returncode, 0, msg=result.stderr)
text = progress.read_text(encoding="utf-8")
self.assertIn(
"- [x] `2026-01-03-demo.md` done: "
"verified: python -m unittest tests.test_main_loop_cli",
text,
)
self.assertIn(
"verification: python -m unittest tests.test_main_loop_cli",
text,
)
def test_finish_without_verified_clears_stale_verification(self):
with tempfile.TemporaryDirectory() as tmp_dir:
root = Path(tmp_dir)
progress = root / "memory-bank" / "progress.md"
progress.parent.mkdir(parents=True)
progress.write_text(
"\n".join(
[
"# Plan 状态",
"",
"<!-- workflow-state:start -->",
"phase: executing",
"plan: docs/superpowers/plans/2026-01-03-demo.md",
"verification: old evidence",
"<!-- workflow-state:end -->",
"",
"<!-- plan-status:start -->",
"- [ ] `2026-01-03-demo.md` in-progress",
"<!-- plan-status:end -->",
"",
]
),
encoding="utf-8",
)
result = run_cli(
"finish",
"-plan",
"docs/superpowers/plans/2026-01-03-demo.md",
"-status",
"blocked",
"-progress",
"memory-bank/progress.md",
"-note",
"needs confirmation",
cwd=root,
)
self.assertEqual(result.returncode, 0, msg=result.stderr)
text = progress.read_text(encoding="utf-8")
self.assertNotIn("verification: old evidence", text)
self.assertIn("phase: blocked", text)
def test_finish_updates_workflow_phase_and_preserves_metadata(self): def test_finish_updates_workflow_phase_and_preserves_metadata(self):
with tempfile.TemporaryDirectory() as tmp_dir: with tempfile.TemporaryDirectory() as tmp_dir:
root = Path(tmp_dir) root = Path(tmp_dir)
@ -712,9 +491,7 @@ class MainLoopCliTests(unittest.TestCase):
root = Path(tmp_dir) root = Path(tmp_dir)
plans_dir = root / "docs" / "superpowers" / "plans" plans_dir = root / "docs" / "superpowers" / "plans"
plans_dir.mkdir(parents=True) plans_dir.mkdir(parents=True)
(plans_dir / "2026-05-18-demo.md").write_text( (plans_dir / "2026-05-18-demo.md").write_text("demo", encoding="utf-8")
valid_plan_text(), encoding="utf-8"
)
progress = root / "memory-bank" / "progress.md" progress = root / "memory-bank" / "progress.md"
progress.parent.mkdir(parents=True) progress.parent.mkdir(parents=True)
@ -784,67 +561,6 @@ class MainLoopCliTests(unittest.TestCase):
) )
self.assertIn("- [x] `2026-05-18-demo.md` done", text) self.assertIn("- [x] `2026-05-18-demo.md` done", text)
def test_status_reports_counts_and_current_workflow_state(self):
with tempfile.TemporaryDirectory() as tmp_dir:
root = Path(tmp_dir)
plans_dir = root / "docs" / "superpowers" / "plans"
plans_dir.mkdir(parents=True)
for plan_key in (
"2026-01-01-a.md",
"2026-01-02-b.md",
"2026-01-03-c.md",
"2026-01-04-d.md",
"2026-01-05-e.md",
):
(plans_dir / plan_key).write_text(valid_plan_text(), encoding="utf-8")
progress = root / "memory-bank" / "progress.md"
progress.parent.mkdir(parents=True)
progress.write_text(
"\n".join(
[
"# 当前进展",
"",
"<!-- workflow-state:start -->",
"phase: executing",
"plan: docs/superpowers/plans/2026-01-02-b.md",
"claimed_by: codex-test",
"<!-- workflow-state:end -->",
"",
"<!-- plan-status:start -->",
"- [ ] `2026-01-01-a.md` pending",
"- [ ] `2026-01-02-b.md` in-progress",
"- [x] `2026-01-03-c.md` done",
"- [ ] `2026-01-04-d.md` blocked: env:linux:Task2",
"- [ ] `2026-01-05-e.md` skipped: obsolete",
"<!-- plan-status:end -->",
"",
]
),
encoding="utf-8",
)
result = run_cli(
"status",
"-plans",
"docs/superpowers/plans",
"-progress",
"memory-bank/progress.md",
cwd=root,
)
self.assertEqual(result.returncode, 0, msg=result.stderr)
self.assertIn(
"STATUS total=5 pending=1 in-progress=1 done=1 blocked=1 skipped=1",
result.stdout,
)
self.assertIn(
"CURRENT phase=executing "
"plan=docs/superpowers/plans/2026-01-02-b.md "
"claimed_by=codex-test",
result.stdout,
)
def test_concurrent_record_preserves_spec_and_plan_metadata(self): def test_concurrent_record_preserves_spec_and_plan_metadata(self):
with tempfile.TemporaryDirectory() as tmp_dir: with tempfile.TemporaryDirectory() as tmp_dir:
root = Path(tmp_dir) root = Path(tmp_dir)

View File

@ -1,44 +0,0 @@
import unittest
from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
README = ROOT / "skills" / "README.md"
class SkillsReadmeTests(unittest.TestCase):
def test_readme_describes_first_party_and_thirdparty_skill_groups(self):
text = README.read_text(encoding="utf-8")
for name in ("commit-message", "gitea-fix-ci", "style-cleanup"):
self.assertIn(name, text)
for name in (
"brainstorming",
"executing-plans",
"systematic-debugging",
"test-driven-development",
"requesting-code-review",
"receiving-code-review",
"ui-ux-pro-max",
"karpathy-guidelines",
):
self.assertIn(name, text)
def test_readme_explains_suite_membership_for_registered_sources(self):
text = README.read_text(encoding="utf-8")
self.assertIn("brooks-lint", text)
self.assertIn("brooks-review", text)
self.assertIn("brooks-audit", text)
self.assertIn("brooks-debt", text)
self.assertIn("brooks-test", text)
self.assertIn("_shared", text)
self.assertIn("codebase-recon", text)
self.assertIn("codebase-migrate", text)
self.assertIn("已登记待同步", text)
if __name__ == "__main__":
unittest.main()

View File

@ -1,33 +0,0 @@
import unittest
from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
CURATION = ROOT / "skills" / "thirdparty" / "thirdparty-skills.yml"
def read_text(path: Path) -> str:
return path.read_text(encoding="utf-8")
class ThirdpartySkillCurationTests(unittest.TestCase):
def test_architecture_skills_are_recorded_for_sync(self):
text = read_text(CURATION)
self.assertIn("id: brooks-lint", text)
self.assertIn("upstream_repo: https://github.com/hyhmrright/brooks-lint", text)
self.assertIn("architecture audit", text)
self.assertIn("sync: enabled", text)
self.assertIn("id: codebase-recon", text)
self.assertIn("upstream_repo: https://github.com/outfitter-dev/agents", text)
self.assertIn("upstream_path: plugins/outfitter/skills/codebase-recon", text)
self.assertIn("risk scan", text)
self.assertIn("id: codebase-migrate", text)
self.assertIn("upstream_repo: https://github.com/ComposioHQ/awesome-codex-skills", text)
self.assertIn("upstream_path: codebase-migrate", text)
self.assertIn("large codebase migrations", text)
if __name__ == "__main__":
unittest.main()

View File

@ -17,8 +17,6 @@ SKILLS_MD = ROOT / "SKILLS.md"
SUPERPOWERS_LIST = ROOT / "skills" / "thirdparty" / ".sources" / "superpowers.list" SUPERPOWERS_LIST = ROOT / "skills" / "thirdparty" / ".sources" / "superpowers.list"
UI_UX_PRO_MAX_LIST = ROOT / "skills" / "thirdparty" / ".sources" / "ui-ux-pro-max.list" UI_UX_PRO_MAX_LIST = ROOT / "skills" / "thirdparty" / ".sources" / "ui-ux-pro-max.list"
UI_UX_PRO_MAX_DIR = ROOT / "skills" / "thirdparty" / "ui-ux-pro-max" UI_UX_PRO_MAX_DIR = ROOT / "skills" / "thirdparty" / "ui-ux-pro-max"
BROOKS_LINT_LIST = ROOT / "skills" / "thirdparty" / ".sources" / "brooks-lint.list"
CODEBASE_RECON_LIST = ROOT / "skills" / "thirdparty" / ".sources" / "codebase-recon.list"
def load_manifest() -> dict: def load_manifest() -> dict:
@ -48,14 +46,7 @@ class ThirdpartySkillsPipelineTests(unittest.TestCase):
data = load_manifest() data = load_manifest()
self.assertEqual( self.assertEqual(
[entry["id"] for entry in data["sources"]], [entry["id"] for entry in data["sources"]],
[ ["superpowers", "ui-ux-pro-max", "andrej-karpathy-skills"],
"superpowers",
"ui-ux-pro-max",
"andrej-karpathy-skills",
"brooks-lint",
"codebase-recon",
"codebase-migrate",
],
) )
def test_karpathy_manifest_uses_copy_skill_dirs_sync_mode(self): def test_karpathy_manifest_uses_copy_skill_dirs_sync_mode(self):
@ -76,39 +67,6 @@ class ThirdpartySkillsPipelineTests(unittest.TestCase):
self.assertEqual(ui_skill["sync_mode"], "render_skill") self.assertEqual(ui_skill["sync_mode"], "render_skill")
self.assertEqual(ui_skill["snapshot_dir"], "ui-ux-pro-max") self.assertEqual(ui_skill["snapshot_dir"], "ui-ux-pro-max")
def test_architecture_skill_sources_use_include_filters(self):
data = load_manifest()
brooks = next(item for item in data["sources"] if item["id"] == "brooks-lint")
self.assertEqual(brooks["sync_mode"], "copy_skill_dirs")
self.assertEqual(brooks["snapshot_dir"], "brooks-lint")
self.assertEqual(brooks["skills_subdir"], "skills")
self.assertEqual(
brooks["source_list"], "skills/thirdparty/.sources/brooks-lint.list"
)
self.assertIn("brooks-audit", brooks["include_skill_dirs"])
self.assertIn("brooks-review", brooks["include_skill_dirs"])
self.assertIn("_shared", brooks["include_skill_dirs"])
recon = next(item for item in data["sources"] if item["id"] == "codebase-recon")
self.assertEqual(recon["sync_mode"], "copy_skill_dirs")
self.assertEqual(recon["snapshot_dir"], "outfitter-agents")
self.assertEqual(recon["skills_subdir"], "plugins/outfitter/skills")
self.assertEqual(
recon["source_list"], "skills/thirdparty/.sources/codebase-recon.list"
)
self.assertEqual(recon["include_skill_dirs"], ["codebase-recon"])
migrate = next(
item for item in data["sources"] if item["id"] == "codebase-migrate"
)
self.assertEqual(migrate["sync_mode"], "copy_skill_dirs")
self.assertEqual(migrate["snapshot_dir"], "awesome-codex-skills")
self.assertEqual(migrate["skills_subdir"], ".")
self.assertEqual(
migrate["source_list"], "skills/thirdparty/.sources/codebase-migrate.list"
)
self.assertEqual(migrate["include_skill_dirs"], ["codebase-migrate"])
def test_superpowers_manifest_prunes_non_superpowers_paths(self): def test_superpowers_manifest_prunes_non_superpowers_paths(self):
data = load_manifest() data = load_manifest()
superpowers = next(item for item in data["sources"] if item["id"] == "superpowers") superpowers = next(item for item in data["sources"] if item["id"] == "superpowers")
@ -180,8 +138,6 @@ class ThirdpartySkillsPipelineTests(unittest.TestCase):
text = SYNC_SCRIPT.read_text(encoding="utf-8") text = SYNC_SCRIPT.read_text(encoding="utf-8")
self.assertIn('"\\x1f".join(', text) self.assertIn('"\\x1f".join(', text)
self.assertIn("while IFS=$'\\x1f' read -r", text) self.assertIn("while IFS=$'\\x1f' read -r", text)
self.assertIn("include_skill_dirs", text)
self.assertIn('skill_dir_included "$name" "$include_skill_dirs"', text)
self.assertNotIn("while IFS=$'\\t' read -r", text) self.assertNotIn("while IFS=$'\\t' read -r", text)
self.assertNotIn("exclude_skill_dirs", text) self.assertNotIn("exclude_skill_dirs", text)
self.assertNotIn("is_excluded_skill_dir", text) self.assertNotIn("is_excluded_skill_dir", text)

View File

@ -3,54 +3,12 @@ from pathlib import Path
ROOT = Path(__file__).resolve().parents[1] ROOT = Path(__file__).resolve().parents[1]
RULESET_TSL = ROOT / "rulesets" / "tsl" / "index.md" RULESET_TSL = ROOT / "rulesets" / "tsl" / "index.md"
TSL_INTRODUCTION = ROOT / "docs" / "tsl" / "syntax" / "01_introduction.md"
TSL_QUICKSTART = ROOT / "docs" / "tsl" / "syntax" / "02_quickstart.md"
TSL_CORE_MODEL = ROOT / "docs" / "tsl" / "syntax" / "03_core_model.md"
TSL_VALUES = ROOT / "docs" / "tsl" / "syntax" / "04_values_and_literals.md"
TSL_VARIABLES = ROOT / "docs" / "tsl" / "syntax" / "05_variables_and_constants.md"
TSL_FUNCTIONS = ROOT / "docs" / "tsl" / "syntax" / "06_functions_and_calls.md"
TSL_EXPRESSIONS = ROOT / "docs" / "tsl" / "syntax" / "07_expressions_and_operators.md"
README = ROOT / "README.md" README = ROOT / "README.md"
PLAYBOOK_EXAMPLE = ROOT / "playbook.toml.example" PLAYBOOK_EXAMPLE = ROOT / "playbook.toml.example"
SKILLS_DOC = ROOT / "SKILLS.md" SKILLS_DOC = ROOT / "SKILLS.md"
TEMPLATES_CI_README = ROOT / "templates" / "ci" / "README.md" TEMPLATES_CI_README = ROOT / "templates" / "ci" / "README.md"
TEMPLATES_README = ROOT / "templates" / "README.md" TEMPLATES_README = ROOT / "templates" / "README.md"
REMOVED_TSL_GUIDE = ROOT / "skills" / "tsl-guide" REMOVED_TSL_GUIDE = ROOT / "skills" / "tsl-guide"
TSL_REFERENCE_INDEX = ROOT / "docs" / "tsl" / "reference" / "index.md"
TSL_REFERENCE_CATALOG_INDEX = ROOT / "docs" / "tsl" / "reference" / "catalog" / "index.md"
TSL_REFERENCE_VERIFIED_INDEX = ROOT / "docs" / "tsl" / "reference" / "verified" / "index.md"
TSL_REFERENCE_VERIFIED_CORE = ROOT / "docs" / "tsl" / "reference" / "verified" / "core.md"
TSL_REFERENCE_UNAVAILABLE = ROOT / "docs" / "tsl" / "reference" / "unavailable_methods.md"
TSL_FINANCE_INDEX = ROOT / "docs" / "tsl" / "finance" / "index.md"
TSL_MODULES_INDEX = ROOT / "docs" / "tsl" / "modules" / "index.md"
TSL_REMAINING_SYNTAX_AGENT_SECTIONS = {
"08_control_flow.md": "Agent 控制流判断流程",
"09_objects_and_classes.md": "Agent 对象/类判断流程",
"10_units_and_scope.md": "Agent unit/作用域判断流程",
"11_runtime_context_and_with.md": "Agent 运行时上下文判断流程",
"12_pitfalls.md": "Agent 常见误写判断流程",
"13_matrix_and_collections.md": "Agent 矩阵/集合判断流程",
"14_resultset_and_filters.md": "Agent 结果集/过滤判断流程",
"15_ts_sql.md": "Agent TS-SQL 判断流程",
"16_debug_and_profiler.md": "Agent 调试/Profiler 判断流程",
"18_lexical_structure_and_compile_options.md": "Agent 词法/编译选项判断流程",
"19_types_and_conversions.md": "Agent 类型/转换判断流程",
"20_strings_and_text.md": "Agent 字符串/文本判断流程",
"21_external_calls_and_threads.md": "Agent 外部调用/线程判断流程",
"22_namespace_libpath_and_unit_runtime.md": "Agent 命名空间/Libpath 判断流程",
"23_object_runtime_and_introspection.md": "Agent 对象运行时/反射判断流程",
"24_builtin_runtime_objects.md": "Agent 内置运行时对象判断流程",
"25_set_operations.md": "Agent 集合运算判断流程",
"26_matrix_deep_dive.md": "Agent 矩阵深水判断流程",
"27_fmarray.md": "Agent FMArray 判断流程",
"28_ts_sql_core.md": "Agent TS-SQL Core 判断流程",
"29_ts_sql_advanced.md": "Agent TS-SQL Advanced 判断流程",
"30_runtime_services_and_global_cache.md": "Agent 运行时服务/全局缓存判断流程",
"31_complex_and_weakref.md": "Agent 复数/弱引用判断流程",
"32_object_overloads_and_iteration.md": "Agent 对象重载/迭代判断流程",
}
class TslEntrypointsConsistencyTests(unittest.TestCase): class TslEntrypointsConsistencyTests(unittest.TestCase):
def test_ruleset_lists_canonical_tsl_layers(self): def test_ruleset_lists_canonical_tsl_layers(self):
@ -106,512 +64,6 @@ class TslEntrypointsConsistencyTests(unittest.TestCase):
self.assertNotIn("tsl-guide", SKILLS_DOC.read_text(encoding="utf-8")) self.assertNotIn("tsl-guide", SKILLS_DOC.read_text(encoding="utf-8"))
self.assertNotIn("$tsl-guide", RULESET_TSL.read_text(encoding="utf-8")) self.assertNotIn("$tsl-guide", RULESET_TSL.read_text(encoding="utf-8"))
def test_tsl_ruleset_describes_agent_first_file_model(self):
text = RULESET_TSL.read_text(encoding="utf-8")
self.assertIn("agent 判断流程", text)
self.assertIn("用户已给出 `.tsl` / `.tsf` 后缀时,后缀就是判断依据", text)
self.assertIn("`.tsl` 是可执行脚本", text)
self.assertIn("语句区按顺序执行", text)
self.assertIn("函数/类声明区", text)
self.assertIn("`.tsf` 是模块/函数扩展文件", text)
self.assertIn("funcext", text)
self.assertIn("仍不明确时向用户确认", text)
self.assertNotIn("`.tsl` 仅 `function`", text)
self.assertNotIn("脚本优先 `.tsl`,可复用扩展优先 `.tsf`", text)
def test_tsl_syntax_docs_keep_script_then_declarations_model(self):
quickstart = (ROOT / "docs/tsl/syntax/02_quickstart.md").read_text(
encoding="utf-8"
)
core_model = (ROOT / "docs/tsl/syntax/03_core_model.md").read_text(
encoding="utf-8"
)
functions = (ROOT / "docs/tsl/syntax/06_functions_and_calls.md").read_text(
encoding="utf-8"
)
pitfalls = (ROOT / "docs/tsl/syntax/12_pitfalls.md").read_text(
encoding="utf-8"
)
for text in (quickstart, core_model, functions, pitfalls):
self.assertIn("语句区", text)
self.assertIn("声明区", text)
self.assertIn("test();", quickstart)
self.assertIn("function test();", quickstart)
self.assertIn("脚本语句后可以接函数声明", functions)
self.assertIn("不要在声明区后面继续写脚本语句", pitfalls)
self.assertNotIn("不要把顶层函数定义和松散语句混写", quickstart)
self.assertNotIn("不要把顶层函数定义和松散语句混写", core_model)
self.assertNotIn("不要把顶层函数定义和松散语句混在同一个文件模型里", functions)
self.assertIn("后缀就是判断依据", quickstart)
self.assertIn("未给后缀", quickstart)
def test_tsl_agent_entrypoints_do_not_require_runtime_verification(self):
entrypoints = [
RULESET_TSL,
ROOT / "docs/tsl/index.md",
ROOT / "docs/tsl/syntax/index.md",
]
forbidden_phrases = [
"用当前解释器验证",
"本地用 `tsl` 验证",
"用 `tsl` 实测",
"最小实测",
"才写最小 `.tsl` / `.tsf` 例子",
]
for path in entrypoints:
text = path.read_text(encoding="utf-8")
for phrase in forbidden_phrases:
self.assertNotIn(phrase, text, msg=f"{phrase!r} found in {path}")
def test_tsl_index_requires_maintainer_verified_examples_before_publication(self):
text = (ROOT / "docs/tsl/index.md").read_text(encoding="utf-8")
self.assertIn("维护者入文档前验证", text)
self.assertIn("代码库样例", text)
self.assertIn("环境验证通过", text)
self.assertIn("不能写成语法事实", text)
self.assertIn("验证过程不写进语法页", text)
self.assertIn("agent 不需要自行执行验证", text)
def test_tsl_introduction_is_agent_first_decision_model(self):
text = TSL_INTRODUCTION.read_text(encoding="utf-8")
self.assertIn("agent", text)
self.assertIn("后缀就是判断依据", text)
self.assertIn("用户未给后缀", text)
self.assertIn("可执行代码对应 `.tsl`", text)
self.assertIn("通用模块对应 `.tsf`", text)
self.assertIn("`.tsl` 可执行脚本第一印象", text)
self.assertIn("`.tsf` 通用模块第一印象", text)
self.assertIn("不要发明语法", text)
self.assertIn("test();", text)
self.assertIn("function test();", text)
self.assertIn("function Test1();", text)
self.assertNotIn("先看顶层主体", text)
self.assertNotIn("顶层主体优先收敛成四类", text)
def test_tsl_quickstart_is_agent_coding_gate(self):
text = TSL_QUICKSTART.read_text(encoding="utf-8")
self.assertIn("Agent 快速落代码流程", text)
self.assertIn("先看用户有没有指定 `.tsl` / `.tsf` 后缀", text)
self.assertIn("再根据交付目标判断 `.tsl` 或 `.tsf`", text)
self.assertIn("只从 `代码块身份:已验证可执行示例` 的骨架起手", text)
self.assertIn("不要发明语法", text)
self.assertIn("不要在声明区后面继续追加脚本语句", text)
def test_tsl_quickstart_labels_observable_outputs(self):
text = TSL_QUICKSTART.read_text(encoding="utf-8")
self.assertIn("代码块身份:已验证输出片段", text)
self.assertIn("```text\ntest\n```", text)
self.assertIn("```text\n5\n```", text)
self.assertIn("```text\ntest1\n```", text)
self.assertIn("```text\nhello\n```", text)
def test_tsl_quickstart_omits_environment_verification_details(self):
text = TSL_QUICKSTART.read_text(encoding="utf-8")
for phrase in [
"Docker",
"docker exec",
"LD_LIBRARY_PATH",
"/data/workspace",
"U22Cli",
"gitea-runner",
]:
self.assertNotIn(phrase, text)
def test_tsl_core_model_is_agent_file_model_decision_page(self):
text = TSL_CORE_MODEL.read_text(encoding="utf-8")
self.assertIn("Agent 文件模型判断流程", text)
self.assertIn("后缀是第一证据", text)
self.assertIn("可执行交付", text)
self.assertIn("可复用扩展交付", text)
self.assertIn("不要把 `.tsl` 写成只有顶层函数的模块", text)
self.assertIn("不要把 `.tsf` 写成会直接执行脚本语句的入口", text)
self.assertIn("没有文档证据时不要发明文件模型", text)
self.assertNotIn("误以为扩展名本身就完全决定能否写函数、类或 `unit`", text)
def test_tsl_core_model_examples_have_verified_outputs(self):
text = TSL_CORE_MODEL.read_text(encoding="utf-8")
self.assertIn("代码块身份:已验证输出片段", text)
self.assertIn("```text\ntest\n```", text)
self.assertIn("```text\n5\n```", text)
self.assertIn("```text\n1\n```", text)
self.assertIn("```text\ninvalid statement\n```", text)
def test_tsl_core_model_omits_environment_verification_details(self):
text = TSL_CORE_MODEL.read_text(encoding="utf-8")
for phrase in [
"Docker",
"docker exec",
"LD_LIBRARY_PATH",
"/data/workspace",
"U22Cli",
"gitea-runner",
]:
self.assertNotIn(phrase, text)
def test_tsl_values_page_is_agent_value_decision_page(self):
text = TSL_VALUES.read_text(encoding="utf-8")
self.assertIn("Agent 值写法判断流程", text)
self.assertIn("普通值优先从整数、实数、普通字符串、布尔和 `array(...)` 起手", text)
self.assertIn("字符串默认用普通字符串", text)
self.assertIn("`L\"\"` / `U\"\"` 只在编码或宽串场景", text)
self.assertIn("先判断要顺序数组还是字符串键表", text)
self.assertIn("顺序数组和二进制缓冲区下标从 `0` 开始", text)
self.assertIn("字符串下标从 `1` 开始", text)
self.assertIn("没有已验证代码块时不要发明值写法", text)
def test_tsl_values_key_examples_have_verified_output_snippets(self):
text = TSL_VALUES.read_text(encoding="utf-8")
self.assertIn("代码块身份:已验证输出片段", text)
self.assertIn("```text\n1\n12.5\nABC\n1\n0\n2\n```", text)
self.assertIn("```text\n10\n20\n0001\nA\nB\nC\n```", text)
self.assertIn("```text\nBCD\n```", text)
self.assertIn("```text\nString index out of bounds\n```", text)
def test_tsl_values_omits_environment_verification_details(self):
text = TSL_VALUES.read_text(encoding="utf-8")
for phrase in [
"Docker",
"docker exec",
"LD_LIBRARY_PATH",
"/data/workspace",
"U22Cli",
"gitea-runner",
]:
self.assertNotIn(phrase, text)
def test_tsl_variables_page_is_agent_binding_decision_page(self):
text = TSL_VARIABLES.read_text(encoding="utf-8")
self.assertIn("Agent 变量/常量判断流程", text)
self.assertIn("普通变量默认直接用 `:=` 首次赋值", text)
self.assertIn("只有用户要求显式声明或遇到 `{$Explicit+}` 时才优先写 `var`", text)
self.assertIn("顶层脚本常量优先用 `const name := value;`", text)
self.assertIn("`const Name = value;` 优先放在函数 `const` 段、`unit` 接口或类成员里", text)
self.assertIn("单变量拆包必须写成 `[name, ] := array(...)`", text)
self.assertIn("没有已验证代码块时不要发明变量/常量写法", text)
def test_tsl_variables_key_examples_have_verified_output_snippets(self):
text = TSL_VARIABLES.read_text(encoding="utf-8")
self.assertIn("代码块身份:已验证输出片段", text)
self.assertIn("```text\n1\n2\n```", text)
self.assertIn("```text\n7\n```", text)
self.assertIn("```text\n1\n3\n```", text)
self.assertIn("```text\n1\n1\n```", text)
self.assertIn("```text\nvariable not defined\n```", text)
def test_tsl_variables_omits_environment_verification_details(self):
text = TSL_VARIABLES.read_text(encoding="utf-8")
for phrase in [
"Docker",
"docker exec",
"LD_LIBRARY_PATH",
"/data/workspace",
"U22Cli",
"gitea-runner",
]:
self.assertNotIn(phrase, text)
def test_tsl_functions_page_is_agent_function_decision_page(self):
text = TSL_FUNCTIONS.read_text(encoding="utf-8")
self.assertIn("Agent 函数/调用判断流程", text)
self.assertIn("先判断当前文件是 `.tsl` 还是 `.tsf`", text)
self.assertIn("`.tsl` 中先写语句区,再把 `function` / `procedure` 声明放在后面", text)
self.assertIn("需要返回值时用 `function`,不需要返回值时用 `procedure`", text)
self.assertIn("命名参数只写 `name: value`", text)
self.assertIn("不要把 `name = value` 当成命名参数", text)
self.assertIn("没有已验证代码块时不要发明函数/调用写法", text)
self.assertNotIn("在我于", text)
def test_tsl_functions_key_examples_have_verified_output_snippets(self):
text = TSL_FUNCTIONS.read_text(encoding="utf-8")
self.assertIn("代码块身份:已验证输出片段", text)
self.assertIn("```text\ntest\n```", text)
self.assertIn("```text\n2\n```", text)
self.assertIn("```text\n12\n```", text)
self.assertIn("```text\ninvalid statement\n```", text)
def test_tsl_functions_omits_environment_verification_details(self):
text = TSL_FUNCTIONS.read_text(encoding="utf-8")
for phrase in [
"Docker",
"docker exec",
"LD_LIBRARY_PATH",
"/data/workspace",
"U22Cli",
"gitea-runner",
]:
self.assertNotIn(phrase, text)
def test_tsl_expressions_page_is_agent_expression_decision_page(self):
text = TSL_EXPRESSIONS.read_text(encoding="utf-8")
self.assertIn("Agent 表达式/运算符判断流程", text)
self.assertIn("先判断要写赋值、比较、条件求值、表达式对象、空安全访问还是链式比较", text)
self.assertIn("赋值只能用 `:=`", text)
self.assertIn("比较才用 `=`", text)
self.assertIn("条件求值优先用 `flag ? true_value : false_value`", text)
self.assertIn("`if condition then true_value else false_value` 必须带 `else`", text)
self.assertIn("没有已验证代码块时不要发明表达式/运算符写法", text)
self.assertNotIn("当前环境里", text)
def test_tsl_expressions_key_examples_have_verified_output_snippets(self):
text = TSL_EXPRESSIONS.read_text(encoding="utf-8")
self.assertIn("代码块身份:已验证输出片段", text)
self.assertIn("```text\n3\n```", text)
self.assertIn("```text\nAB\n```", text)
self.assertIn("```text\n222888\n1\n1\n1\n1\n```", text)
self.assertIn("```text\n1\n0\n```", text)
self.assertIn("```text\n2\n```", text)
self.assertIn("```text\n6\n```", text)
self.assertIn("```text\n1\n7\n1\n```", text)
self.assertIn("```text\ninvalid statement\n```", text)
def test_tsl_expressions_omits_environment_verification_details(self):
text = TSL_EXPRESSIONS.read_text(encoding="utf-8")
for phrase in [
"Docker",
"docker exec",
"LD_LIBRARY_PATH",
"/data/workspace",
"U22Cli",
"gitea-runner",
]:
self.assertNotIn(phrase, text)
def test_remaining_tsl_syntax_pages_are_agent_decision_pages(self):
syntax_root = ROOT / "docs" / "tsl" / "syntax"
for filename, section in TSL_REMAINING_SYNTAX_AGENT_SECTIONS.items():
with self.subTest(filename=filename):
text = (syntax_root / filename).read_text(encoding="utf-8")
self.assertIn(section, text)
self.assertIn("agent", text.lower())
self.assertIn("不要发明", text)
def test_remaining_tsl_syntax_pages_have_verified_output_snippets(self):
syntax_root = ROOT / "docs" / "tsl" / "syntax"
for filename in TSL_REMAINING_SYNTAX_AGENT_SECTIONS:
with self.subTest(filename=filename):
text = (syntax_root / filename).read_text(encoding="utf-8")
if (
"代码块身份:已验证可执行示例" in text
or "代码块身份:反例 / 不可照写" in text
):
self.assertIn("代码块身份:已验证输出片段", text)
def test_remaining_tsl_syntax_pages_omit_personal_and_environment_details(self):
syntax_root = ROOT / "docs" / "tsl" / "syntax"
forbidden_phrases = [
"我在",
"当前环境里",
"Docker",
"docker exec",
"LD_LIBRARY_PATH",
"/data/workspace",
"U22Cli",
"gitea-runner",
]
for filename in TSL_REMAINING_SYNTAX_AGENT_SECTIONS:
with self.subTest(filename=filename):
text = (syntax_root / filename).read_text(encoding="utf-8")
for phrase in forbidden_phrases:
self.assertNotIn(phrase, text)
def test_tsl_reference_index_requires_verified_function_workflow(self):
text = TSL_REFERENCE_INDEX.read_text(encoding="utf-8")
self.assertIn("Agent 函数使用规则", text)
self.assertIn("只从 verified 函数页读取参数类型", text)
self.assertIn("catalog 只是候选函数索引", text)
self.assertIn("确认每个方法的参数类型", text)
self.assertIn("verified/index.md", text)
self.assertIn("verified/core.md", text)
self.assertIn("unavailable_methods.md", text)
self.assertIn("不能把 catalog 里的函数名直接当成可调用事实", text)
self.assertNotIn("verification_failures", text)
self.assertNotIn("函数入库验证流程", text)
self.assertNotIn("验证过程", text)
self.assertNotIn("代码块身份", text)
self.assertNotIn("已验证输出片段", text)
self.assertNotIn("验证失败", text)
def test_tsl_reference_catalog_is_candidate_index_not_callable_fact(self):
catalog_index = TSL_REFERENCE_CATALOG_INDEX.read_text(encoding="utf-8")
self.assertIn("候选函数索引", catalog_index)
self.assertIn("候选名没有进入 verified 函数页前不能当成可调用事实", catalog_index)
self.assertIn("../verified/index.md", catalog_index)
self.assertIn("../verified/core.md", catalog_index)
self.assertIn("只从 verified 函数页读取参数类型", catalog_index)
self.assertNotIn("错误参数组合", catalog_index)
self.assertNotIn("参数验证", catalog_index)
for path in (ROOT / "docs" / "tsl" / "reference" / "catalog").glob("*.md"):
text = path.read_text(encoding="utf-8")
self.assertIn("候选函数索引", text, msg=f"{path.name} missing candidate warning")
self.assertIn("只从 verified 函数页读取参数类型", text)
def test_tsl_reference_verified_index_routes_to_parameter_fact_pages(self):
text = TSL_REFERENCE_VERIFIED_INDEX.read_text(encoding="utf-8")
self.assertIn("文档类型agent 参数事实索引", text)
self.assertIn("core.md", text)
self.assertIn("Abs", text)
self.assertIn("ifInt", text)
self.assertIn("DateToStr", text)
self.assertIn("Length", text)
self.assertIn("只从具体函数页读取接收类型", text)
self.assertNotIn("验证过程", text)
self.assertNotIn("代码块身份", text)
def test_tsl_reference_verified_core_records_agent_parameter_facts(self):
text = TSL_REFERENCE_VERIFIED_CORE.read_text(encoding="utf-8")
self.assertIn("文档类型agent 参数事实表", text)
self.assertIn("接收类型", text)
self.assertIn("返回", text)
self.assertIn("`Abs(value)`", text)
self.assertIn("`ifInt(value)`", text)
self.assertIn("`DateToStr(value)`", text)
self.assertIn("`Length(value)`", text)
self.assertIn("整数", text)
self.assertIn("实数", text)
self.assertIn("字符串", text)
self.assertIn("数组", text)
self.assertIn("日期时间", text)
self.assertNotIn("代码块身份", text)
self.assertNotIn("已验证输出片段", text)
self.assertNotIn("Function Abs execution error", text)
self.assertNotIn("错误参数", text)
def test_tsl_reference_all_markdown_avoid_verification_logs(self):
for path in (ROOT / "docs" / "tsl" / "reference").rglob("*.md"):
text = path.read_text(encoding="utf-8")
with self.subTest(path=path.relative_to(ROOT)):
self.assertNotIn("验证过程", text)
self.assertNotIn("函数入库验证流程", text)
self.assertNotIn("代码块身份", text)
self.assertNotIn("已验证输出片段", text)
self.assertNotIn("Function Abs execution error", text)
def test_tsl_reference_unavailable_records_only_unavailable_methods(self):
text = TSL_REFERENCE_UNAVAILABLE.read_text(encoding="utf-8")
self.assertIn("文档类型:当前测试环境不支持的方法清单", text)
self.assertIn("当前测试环境不支持的方法", text)
self.assertIn("暂无已入档记录", text)
self.assertNotIn("failure", text.lower())
self.assertNotIn("失败", text)
self.assertNotIn("验证过程", text)
self.assertNotIn("参数类型错误", text)
self.assertNotIn("`Abs(\"-3\")`", text)
self.assertNotIn("`Abs()`", text)
self.assertNotIn("`DateToStr(\"2011-12-31\")`", text)
self.assertNotIn("`DateToStr()`", text)
self.assertNotIn("`Length(123)`", text)
self.assertNotIn("Function Abs execution error", text)
def test_tsl_reference_docs_do_not_name_valid_parameter_mismatches_as_failures(self):
reference_root = ROOT / "docs" / "tsl" / "reference"
for path in reference_root.rglob("*.md"):
text = path.read_text(encoding="utf-8")
with self.subTest(path=path.relative_to(ROOT)):
self.assertNotIn("verification_failures", text)
self.assertNotIn("错误参数", text)
self.assertNotIn("验证失败", text)
self.assertNotIn("Abs(\"-3\")", text)
self.assertNotIn("DateToStr(\"2011-12-31\")", text)
self.assertNotIn("Length(123)", text)
def test_tsl_finance_docs_are_agent_business_decision_pages(self):
finance_root = ROOT / "docs" / "tsl" / "finance"
for path in finance_root.glob("*.md"):
text = path.read_text(encoding="utf-8")
with self.subTest(path=path.name):
self.assertIn("Agent", text)
self.assertIn("不要发明", text)
self.assertIn("项目实际接口", text)
self.assertNotIn("是否含已验证", text)
self.assertNotIn("代码块身份", text)
self.assertNotIn("验证过程", text)
def test_tsl_finance_index_routes_all_finance_pages(self):
text = TSL_FINANCE_INDEX.read_text(encoding="utf-8")
self.assertIn("Agent Finance 路由规则", text)
self.assertIn("entry_decision.md", text)
self.assertIn("market_data_context.md", text)
self.assertIn("series_and_indicator_model.md", text)
self.assertIn("selection_and_signal_patterns.md", text)
self.assertIn("backtest_and_trade_flow.md", text)
def test_tsl_modules_docs_are_agent_integration_boundary_pages(self):
modules_root = ROOT / "docs" / "tsl" / "modules"
for path in modules_root.glob("*.md"):
text = path.read_text(encoding="utf-8")
with self.subTest(path=path.name):
self.assertIn("Agent", text)
self.assertIn("不要发明", text)
self.assertNotIn("是否含已验证", text)
self.assertNotIn("代码块身份", text)
self.assertNotIn("验证过程", text)
def test_tsl_modules_index_routes_all_module_pages(self):
text = TSL_MODULES_INDEX.read_text(encoding="utf-8")
self.assertIn("Agent Modules 路由规则", text)
self.assertIn("tsbacktesting.md", text)
self.assertIn("tsl_python_interop.md", text)
self.assertIn("wechat_message.md", text)
self.assertIn("pytsl_api.md", text)
def test_tsl_finance_and_modules_omit_environment_verification_details(self):
forbidden_phrases = [
"Docker",
"docker exec",
"LD_LIBRARY_PATH",
"/data/workspace",
"U22Cli",
"gitea-runner",
"当前环境里",
"实测",
]
for root_name in ("finance", "modules"):
for path in (ROOT / "docs" / "tsl" / root_name).glob("*.md"):
text = path.read_text(encoding="utf-8")
for phrase in forbidden_phrases:
self.assertNotIn(phrase, text, msg=f"{phrase!r} found in {path}")
if __name__ == "__main__": if __name__ == "__main__":
unittest.main() unittest.main()