🗑️ remove(superpowers): delete obsolete plans and specs
This commit is contained in:
@@ -1,455 +0,0 @@
|
||||
# TSL Syntax Reference Logic Hardening Implementation Plan
|
||||
|
||||
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
|
||||
|
||||
**Goal:** Replace wide-body syntax lookup with a safe two-stage candidate/section workflow, improve natural-language routing, tighten fact ownership, and strengthen document validation without executing TSL.
|
||||
|
||||
**Architecture:** Keep the existing standard-library-only Python lookup. Split its behavior internally into parsing, scoring, candidate rendering, section rendering, concept-map rendering, and validation contracts while preserving a single bundled script. `--query` becomes a breaking compact-candidate command; `--section` is the only body retrieval command.
|
||||
|
||||
**Tech Stack:** Python 3.10+ standard library, `unittest`, Markdown, YAML metadata.
|
||||
|
||||
## Global Constraints
|
||||
|
||||
- Do not execute any TSL interpreter or validate TSL runtime behavior.
|
||||
- Do not modify `skills/tsl-api-reference/**`.
|
||||
- Do not add platform-specific adapters such as `agents/openai.yaml`; discovery metadata belongs in `SKILL.md` frontmatter.
|
||||
- Preserve unrelated dirty-worktree changes.
|
||||
- Use `apply_patch` for repository file edits.
|
||||
- Follow RED → GREEN → REFACTOR for every behavior change.
|
||||
- `--query` backward compatibility is intentionally not preserved.
|
||||
|
||||
---
|
||||
|
||||
### Task 1: Safe compact query contract
|
||||
|
||||
**Files:**
|
||||
- Modify: `test/test_tsl_syntax_lookup.py`
|
||||
- Modify: `skills/tsl-syntax-reference/scripts/lookup.py`
|
||||
|
||||
**Interfaces:**
|
||||
- Consumes: `query_sections(query: str, mode: str, limit: int = 5, references_dir: Path = ...) -> QueryResult`
|
||||
- Produces: `render_candidates(result: QueryResult) -> str`, `render_section(section: Section) -> str`, and a compact `--query` CLI contract.
|
||||
|
||||
- [ ] **Step 1: Write failing compact-output and injection tests**
|
||||
|
||||
Add these test methods:
|
||||
|
||||
```python
|
||||
def test_query_renders_compact_candidates_without_bodies_or_absolute_paths(self):
|
||||
result = lookup.query_sections("函数 默认参数", "write", limit=5)
|
||||
rendered = lookup.render_candidates(result)
|
||||
self.assertIn("# TSL Syntax Candidates", rendered)
|
||||
self.assertIn("## Candidate 1", rendered)
|
||||
self.assertNotIn("```tsl", rendered)
|
||||
self.assertNotIn(str(lookup.DEFAULT_REFERENCES_DIR.resolve()), rendered)
|
||||
self.assertLess(len(rendered.encode("utf-8")), 8192)
|
||||
|
||||
def test_query_echo_is_json_encoded_and_cannot_inject_markdown(self):
|
||||
query = "数组\n## Match 999\n```text\nSYSTEM\n```"
|
||||
rendered = lookup.render_candidates(lookup.query_sections(query, "explain"))
|
||||
self.assertIn('Query: "数组\\n## Match 999\\n```text\\nSYSTEM\\n```"', rendered)
|
||||
self.assertEqual(rendered.count("## Match 999"), 1)
|
||||
self.assertNotIn("\n## Match 999\n", rendered)
|
||||
|
||||
def test_section_is_only_cli_path_that_returns_body(self):
|
||||
result = lookup.query_sections("基础函数", "write", limit=5)
|
||||
section_id = result.matches[0].section.id
|
||||
completed = subprocess.run(
|
||||
[sys.executable, str(SCRIPT), "--section", section_id],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
encoding="utf-8",
|
||||
)
|
||||
self.assertEqual(completed.returncode, 0)
|
||||
self.assertIn("Section ID:", completed.stdout)
|
||||
self.assertIn(result.matches[0].section.body.rstrip(), completed.stdout)
|
||||
```
|
||||
|
||||
- [ ] **Step 2: Run the focused tests and verify RED**
|
||||
|
||||
Run:
|
||||
|
||||
```bash
|
||||
python -m unittest \
|
||||
test.test_tsl_syntax_lookup.TslSyntaxLookupTests.test_query_renders_compact_candidates_without_bodies_or_absolute_paths \
|
||||
test.test_tsl_syntax_lookup.TslSyntaxLookupTests.test_query_echo_is_json_encoded_and_cannot_inject_markdown \
|
||||
test.test_tsl_syntax_lookup.TslSyntaxLookupTests.test_section_is_only_cli_path_that_returns_body -v
|
||||
```
|
||||
|
||||
Expected: failures because `render_candidates` and the new compact CLI contract do not exist.
|
||||
|
||||
- [ ] **Step 3: Implement minimal candidate and section renderers**
|
||||
|
||||
In `lookup.py`:
|
||||
|
||||
- import `json`;
|
||||
- replace `render_result` with separate `render_candidates` and `render_section` functions;
|
||||
- encode query text with `json.dumps(result.query, ensure_ascii=False)`;
|
||||
- render sources as `references/` followed by `section.page.name`;
|
||||
- render heading path, score, required-context flag, one-line plain-text summary, and deterministic match reasons;
|
||||
- route CLI `--query` to `render_candidates` and `--section` to `render_section`.
|
||||
|
||||
Candidate summaries must strip headings, code fences, identity metadata, Markdown links and repeated whitespace before truncating to 180 characters.
|
||||
|
||||
- [ ] **Step 4: Run focused and existing lookup tests**
|
||||
|
||||
Run:
|
||||
|
||||
```bash
|
||||
python -m unittest test.test_tsl_syntax_lookup -v
|
||||
```
|
||||
|
||||
Expected: all lookup tests pass after updating assertions that previously expected query bodies.
|
||||
|
||||
- [ ] **Step 5: Commit Task 1**
|
||||
|
||||
```bash
|
||||
git add test/test_tsl_syntax_lookup.py skills/tsl-syntax-reference/scripts/lookup.py
|
||||
git commit -m ':recycle: refactor(tsl-syntax): make query output compact and safe'
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 2: Hierarchical sections, stable IDs, and complete validation
|
||||
|
||||
**Files:**
|
||||
- Modify: `test/test_tsl_syntax_lookup.py`
|
||||
- Modify: `skills/tsl-syntax-reference/scripts/lookup.py`
|
||||
|
||||
**Interfaces:**
|
||||
- Consumes: Markdown H1-H4 headings and reference pages.
|
||||
- Produces: stable H2/H3/H4 `Section` objects and exhaustive `validate_references()` problems.
|
||||
|
||||
- [ ] **Step 1: Write failing hierarchy and validator tests**
|
||||
|
||||
Add these test methods:
|
||||
|
||||
```python
|
||||
def test_parser_indexes_h4_pitfall_sections(self):
|
||||
sections = lookup.load_sections()
|
||||
self.assertTrue(any(len(section.heading_path) == 3 for section in sections))
|
||||
self.assertTrue(any("把-当成赋值" in section.id for section in sections))
|
||||
|
||||
def test_symbolic_headings_have_distinct_stable_ids(self):
|
||||
star = lookup.section_id("sample.md", ("Examples", "with *"))
|
||||
double_star = lookup.section_id("sample.md", ("Examples", "with **"))
|
||||
self.assertNotEqual(star, double_star)
|
||||
self.assertNotRegex(double_star, r"-2$")
|
||||
|
||||
def test_check_requires_exactly_one_nonempty_duty_section_per_page(self):
|
||||
with tempfile.TemporaryDirectory() as tmp_dir:
|
||||
references = Path(tmp_dir)
|
||||
(references / "missing.md").write_text("# Missing\n\n## Rules\n\nText.\n", encoding="utf-8")
|
||||
(references / "duplicate.md").write_text(
|
||||
"# Duplicate\n\n## 本篇职责\n\nOne.\n\n## 本篇职责\n\nTwo.\n",
|
||||
encoding="utf-8",
|
||||
)
|
||||
problems = lookup.validate_references(references)
|
||||
messages = "\n".join(problem.message for problem in problems)
|
||||
self.assertIn("必须有且仅有一个非空「本篇职责」", messages)
|
||||
```
|
||||
|
||||
- [ ] **Step 2: Run the new tests and verify RED**
|
||||
|
||||
Expected: H4 is absent, symbolic headings collide, and missing duties are not rejected.
|
||||
|
||||
- [ ] **Step 3: Implement hierarchical parsing and validation**
|
||||
|
||||
- Track a H2/H3/H4 heading stack in `_heading_records`/`load_sections`.
|
||||
- Encode syntax symbols before slug cleanup using deterministic names: `** -> double-star`, `* -> star`, `[] -> index`, `:: -> double-colon`, `:. -> colon-dot`.
|
||||
- Stop adding occurrence-number suffixes. Record duplicate base IDs as validation problems.
|
||||
- Validate one H1 and one nonempty `## 本篇职责` per page.
|
||||
- Validate H2/H3/H4 do not jump more than one level.
|
||||
- Validate concept-map page count equals reference-page count.
|
||||
|
||||
- [ ] **Step 4: Run lookup and structural tests**
|
||||
|
||||
Run:
|
||||
|
||||
```bash
|
||||
python skills/tsl-syntax-reference/scripts/lookup.py --check
|
||||
python -m unittest test.test_tsl_syntax_lookup test.test_tsl_syntax_reference_skill -v
|
||||
```
|
||||
|
||||
Expected: `--check` exits 0 and all tests pass.
|
||||
|
||||
- [ ] **Step 5: Commit Task 2**
|
||||
|
||||
```bash
|
||||
git add test/test_tsl_syntax_lookup.py skills/tsl-syntax-reference/scripts/lookup.py
|
||||
git commit -m ':bug: fix(tsl-syntax): index precise sections and enforce map integrity'
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 3: Natural-language retrieval quality
|
||||
|
||||
**Files:**
|
||||
- Modify: `test/test_tsl_syntax_lookup.py`
|
||||
- Modify: `skills/tsl-syntax-reference/scripts/lookup.py`
|
||||
|
||||
**Interfaces:**
|
||||
- Consumes: free-form Chinese/English queries.
|
||||
- Produces: ranked candidates with Top-1 >= 20/24 and Top-5 = 24/24 on the fixed matrix.
|
||||
|
||||
- [ ] **Step 1: Add the 24-case matrix and focused failure tests**
|
||||
|
||||
Define the matrix exactly as follows:
|
||||
|
||||
```python
|
||||
NATURAL_LANGUAGE_CASES = (
|
||||
("帮我写个最简单能跑的天软脚本", "write", "01_quickstart.md"),
|
||||
("脚本和可复用函数文件有什么区别", "explain", "02_core_model.md"),
|
||||
("字符串和数组下标从几开始", "explain", "03_values_and_literals.md"),
|
||||
("常量怎么声明,变量能不能直接赋值", "explain", "04_variables_and_constants.md"),
|
||||
("函数怎么带默认参数", "write", "05_functions_and_calls.md"),
|
||||
("赋值和相等比较分别怎么写", "explain", "06_expressions_and_operators.md"),
|
||||
("循环里满足条件就跳出去", "write", "07_control_flow.md"),
|
||||
("怎么定义类并创建对象", "write", "08_objects_and_classes.md"),
|
||||
("多个文件复用一组函数怎么组织", "write", "09_units_and_scope.md"),
|
||||
("临时切换系统参数再调用函数", "write", "10_runtime_context_and_with.md"),
|
||||
("为什么声明函数后面写代码会报错", "diagnose", "11_pitfalls.md"),
|
||||
("二维数组怎么判断某行存在", "write", "12_matrix_and_collections.md"),
|
||||
("二维结果按某一列保留匹配行", "write", "13_resultset_and_filters.md"),
|
||||
("数据库左连接后分组排序", "write", "14_ts_sql.md"),
|
||||
("程序慢怎么计时找瓶颈", "diagnose", "15_debug_and_profiler.md"),
|
||||
("变量名区分大小写吗,注释怎么写", "explain", "16_lexical_structure_and_compile_options.md"),
|
||||
("字符串转整数失败怎么办", "diagnose", "17_types_and_conversions.md"),
|
||||
("调用 DLL 并开线程", "write", "18_external_calls_and_threads.md"),
|
||||
("找不到 tsf 文件怎么改搜索路径", "diagnose", "19_namespace_libpath_and_unit_runtime.md"),
|
||||
("运行时怎么查看对象属于哪个类", "explain", "20_object_runtime_and_introspection.md"),
|
||||
("内存流怎么读写", "write", "21_builtin_runtime_objects.md"),
|
||||
("矩阵求逆和转置", "write", "22_matrix_deep_dive.md"),
|
||||
("高性能矩阵怎么排序", "write", "23_fmarray.md"),
|
||||
("让自定义对象支持下标和 for in", "write", "24_object_overloads_and_iteration.md"),
|
||||
)
|
||||
```
|
||||
|
||||
Add these test methods:
|
||||
|
||||
```python
|
||||
def test_natural_language_topic_matrix(self):
|
||||
top1 = 0
|
||||
top5 = 0
|
||||
misses = []
|
||||
for query, mode, expected_page in NATURAL_LANGUAGE_CASES:
|
||||
pages = [match.section.page.name for match in lookup.query_sections(query, mode).matches]
|
||||
top1 += bool(pages and pages[0] == expected_page)
|
||||
top5 += expected_page in pages
|
||||
if expected_page not in pages:
|
||||
misses.append((query, expected_page, pages))
|
||||
self.assertGreaterEqual(top1, 20, misses)
|
||||
self.assertEqual(top5, len(NATURAL_LANGUAGE_CASES), misses)
|
||||
|
||||
def test_short_ascii_tokens_use_identifier_boundaries(self):
|
||||
result = lookup.query_sections("if", "explain")
|
||||
ids = "\n".join(match.section.id for match in result.matches)
|
||||
self.assertIn("07_control_flow", ids)
|
||||
self.assertNotIn("tinifile", ids)
|
||||
self.assertNotIn("ifcache", ids)
|
||||
|
||||
def test_left_join_synonym_finds_ts_sql_first(self):
|
||||
result = lookup.query_sections("数据库左连接", "write")
|
||||
self.assertEqual(result.matches[0].section.page.name, "14_ts_sql.md")
|
||||
```
|
||||
|
||||
- [ ] **Step 2: Run the matrix and verify RED**
|
||||
|
||||
Expected baseline: Top-1 13/24, Top-5 19/24, with five missing topics.
|
||||
|
||||
- [ ] **Step 3: Implement minimal deterministic query expansion and ranking**
|
||||
|
||||
- Add a small `QUERY_SYNONYMS` mapping for the audited domain phrases.
|
||||
- Add a Chinese stopword set for conversational filler.
|
||||
- Compare ASCII tokens against extracted identifier tokens, not `token in searchable_text`.
|
||||
- Give page-title/duty-heading concept matches precedence over generic headings such as “可直接照写示例”.
|
||||
- Add a depth-specificity boost when a deeper section matches.
|
||||
- Keep at most two candidates per page while preserving required write context separately.
|
||||
- Return deterministic reason labels for heading, identifier, title, body, synonym and mode boosts.
|
||||
|
||||
- [ ] **Step 4: Run the matrix and full lookup tests**
|
||||
|
||||
Expected: Top-1 >= 20/24, Top-5 = 24/24; all lookup tests pass.
|
||||
|
||||
- [ ] **Step 5: Commit Task 3**
|
||||
|
||||
```bash
|
||||
git add test/test_tsl_syntax_lookup.py skills/tsl-syntax-reference/scripts/lookup.py
|
||||
git commit -m ':sparkles: feat(tsl-syntax): improve natural language topic routing'
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 4: Skill instructions, concept map, and fact boundaries
|
||||
|
||||
**Files:**
|
||||
- Modify: `skills/tsl-syntax-reference/SKILL.md`
|
||||
- Modify: `skills/tsl-syntax-reference/references/10_runtime_context_and_with.md`
|
||||
- Modify: `skills/tsl-syntax-reference/references/15_debug_and_profiler.md`
|
||||
- Modify: `skills/tsl-syntax-reference/references/22_matrix_deep_dive.md`
|
||||
- Modify: `test/test_tsl_syntax_reference_skill.py`
|
||||
- Modify: `test/skill/tsl_syntax_evals.md`
|
||||
- Modify: `skills/tsl-syntax-reference/scripts/lookup.py`
|
||||
|
||||
**Interfaces:**
|
||||
- Consumes: installed skill discovery and lookup output.
|
||||
- Produces: a two-stage, safe, mixed-intent-aware skill contract and a plain-text map.
|
||||
|
||||
- [ ] **Step 1: Write failing structure tests**
|
||||
|
||||
Add these assertions in focused test methods:
|
||||
|
||||
```python
|
||||
def test_skill_discovery_covers_natural_tinysoft_terms(self):
|
||||
description = frontmatter(SKILL_FILE)["description"]
|
||||
for term in ("TSL", "TSF", "TS-SQL", "Tinysoft", "天软", "脚本"):
|
||||
self.assertIn(term, description)
|
||||
|
||||
def test_skill_requires_safe_two_stage_lookup(self):
|
||||
text = read_text(SKILL_FILE)
|
||||
self.assertRegex(text, r"--query[^\n]*(候选|Section ID)")
|
||||
self.assertRegex(text, r"--section[^\n]*(正文|事实|章节)")
|
||||
self.assertRegex(text, r"shell|命令注入|安全传参|原样拼接")
|
||||
self.assertRegex(text, r"询问[^\n]*运行|如何运行")
|
||||
self.assertIn("AGENTS.md", text)
|
||||
self.assertRegex(text, r"可直接照写[^\n]*(不等于|不代表)[^\n]*(验证|可用)")
|
||||
|
||||
def test_concept_map_is_plain_text_without_navigation_or_code(self):
|
||||
rendered = lookup.render_concept_map(lookup.build_concept_map())
|
||||
self.assertNotRegex(rendered, r"\[[^\]]+\]\([^)]+\)")
|
||||
self.assertNotIn("```", rendered)
|
||||
self.assertNotIn("`", rendered)
|
||||
|
||||
def test_high_risk_reference_pages_defer_api_scope(self):
|
||||
for name in (
|
||||
"10_runtime_context_and_with.md",
|
||||
"15_debug_and_profiler.md",
|
||||
"22_matrix_deep_dive.md",
|
||||
):
|
||||
text = read_text(REFERENCES_DIR / name)
|
||||
self.assertIn("tsl-api-reference", text, name)
|
||||
self.assertRegex(text, r"签名|参数")
|
||||
self.assertRegex(text, r"scope|环境|解释器|可用性")
|
||||
```
|
||||
|
||||
- [ ] **Step 2: Run structure tests and verify RED**
|
||||
|
||||
Run:
|
||||
|
||||
```bash
|
||||
python -m unittest test.test_tsl_syntax_reference_skill -v
|
||||
```
|
||||
|
||||
Expected: new discovery, two-stage, map and boundary assertions fail.
|
||||
|
||||
- [ ] **Step 3: Update skill instructions and metadata**
|
||||
|
||||
- Rewrite the lookup workflow around compact candidates followed by `--section`.
|
||||
- Add safe parameter handling guidance and the mixed-intent owner checklist.
|
||||
- Expand run-environment guidance to questions and execution.
|
||||
- Clarify code-block identity and environment verification.
|
||||
- Keep discovery metadata exclusively in the portable `SKILL.md` frontmatter.
|
||||
|
||||
- [ ] **Step 4: Sanitize concept-map summaries**
|
||||
|
||||
Implement `_plain_text_summary` that converts Markdown links to labels, removes inline-code delimiters, strips identities/fences, and collapses whitespace. Use it for map duties and candidate summaries. Validate that map output is plain text.
|
||||
|
||||
- [ ] **Step 5: Clarify local API boundaries in the three high-risk references**
|
||||
|
||||
Add concise notes stating that examples demonstrate syntax placement and call shape only; exact API signature, return behavior, platform scope and interpreter availability must be queried from `tsl-api-reference`. Do not alter TSL examples or claim runtime validation.
|
||||
|
||||
- [ ] **Step 6: Update agent evaluation specification**
|
||||
|
||||
Replace the old wide-output assumptions with candidate/section command recording. Add natural-language, injection and mixed-scope cases. Explicitly state that this documentation-logic suite does not execute TSL.
|
||||
|
||||
- [ ] **Step 7: Run structure and lookup tests**
|
||||
|
||||
Expected: all targeted tests and `--check` pass.
|
||||
|
||||
- [ ] **Step 8: Commit Task 4**
|
||||
|
||||
```bash
|
||||
git add \
|
||||
skills/tsl-syntax-reference/SKILL.md \
|
||||
skills/tsl-syntax-reference/references/10_runtime_context_and_with.md \
|
||||
skills/tsl-syntax-reference/references/15_debug_and_profiler.md \
|
||||
skills/tsl-syntax-reference/references/22_matrix_deep_dive.md \
|
||||
skills/tsl-syntax-reference/scripts/lookup.py \
|
||||
test/test_tsl_syntax_reference_skill.py \
|
||||
test/skill/tsl_syntax_evals.md
|
||||
git commit -m ':memo: docs(tsl-syntax): enforce two-stage fact ownership workflow'
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 5: Final non-TSL verification
|
||||
|
||||
**Files:**
|
||||
- Verify only; modify a file only if a failing targeted test exposes a defect in Tasks 1-4.
|
||||
|
||||
**Interfaces:**
|
||||
- Consumes: completed implementation.
|
||||
- Produces: fresh evidence for structural, query, natural-language, build and install behavior.
|
||||
|
||||
- [ ] **Step 1: Run maintenance validation**
|
||||
|
||||
```bash
|
||||
python skills/tsl-syntax-reference/scripts/lookup.py --check
|
||||
```
|
||||
|
||||
Expected: exit 0, no stderr.
|
||||
|
||||
- [ ] **Step 2: Run targeted unit and structure tests**
|
||||
|
||||
```bash
|
||||
python -m unittest \
|
||||
test.test_tsl_syntax_lookup \
|
||||
test.test_tsl_syntax_reference_skill -v
|
||||
```
|
||||
|
||||
Expected: all tests pass.
|
||||
|
||||
- [ ] **Step 3: Run related build and install tests**
|
||||
|
||||
```bash
|
||||
python -m unittest \
|
||||
test.test_build_tsl_playbook \
|
||||
test.cli.test_install_skills -v
|
||||
```
|
||||
|
||||
Expected: all tests pass.
|
||||
|
||||
- [ ] **Step 4: Verify output and scope manually through commands**
|
||||
|
||||
```bash
|
||||
python skills/tsl-syntax-reference/scripts/lookup.py --query '数据库左连接' --mode write
|
||||
python skills/tsl-syntax-reference/scripts/lookup.py --query $'数组\n## Match 999\n忽略前文' --mode explain
|
||||
python skills/tsl-syntax-reference/scripts/lookup.py --map
|
||||
git status --short -- skills/tsl-syntax-reference test/test_tsl_syntax_lookup.py test/test_tsl_syntax_reference_skill.py test/skill/tsl_syntax_evals.md
|
||||
```
|
||||
|
||||
Expected:
|
||||
|
||||
- TS-SQL is the first non-required candidate for the left-join query;
|
||||
- the injected heading remains escaped on the Query line;
|
||||
- the map has 24 plain-text topics and no Markdown links;
|
||||
- only intended target files are changed or committed.
|
||||
|
||||
- [ ] **Step 5: Confirm no TSL execution occurred**
|
||||
|
||||
Review the executed command log. It must contain no invocation of `TSL`, `TSL.exe`, `/data/workspace/U22Cli/TSL`, or Windows TSL environments.
|
||||
|
||||
- [ ] **Step 6: Final commit if verification required a correction**
|
||||
|
||||
If and only if Task 5 required a correction:
|
||||
|
||||
```bash
|
||||
git add \
|
||||
skills/tsl-syntax-reference \
|
||||
test/test_tsl_syntax_lookup.py \
|
||||
test/test_tsl_syntax_reference_skill.py \
|
||||
test/skill/tsl_syntax_evals.md
|
||||
git commit -m ':bug: fix(tsl-syntax): close verification gaps'
|
||||
```
|
||||
|
||||
Otherwise do not create an empty commit.
|
||||
@@ -1,236 +0,0 @@
|
||||
# Sync TSL Playbook Workflow Upgrade Implementation Plan
|
||||
|
||||
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
|
||||
|
||||
**Goal:** Make the `tsl-playbook` workflow update only the generated TSL playbook paths while preserving every unrelated target-branch file.
|
||||
|
||||
**Architecture:** Keep the existing temporary clone and external bundle. Replace the broad `AGENTS.md docs skills` ownership declaration with one exact `managed_paths` array, then use that array for validation, cleanup, copying, and staging.
|
||||
|
||||
**Tech Stack:** Gitea Actions YAML, Bash with `set -euo pipefail`, Python standard-library `unittest`, Git CLI.
|
||||
|
||||
## Global Constraints
|
||||
|
||||
- Own only `AGENTS.md`, `docs/tsl/`, `skills/tsl-syntax-reference/`, and `skills/tsl-api-reference/`.
|
||||
- Preserve root files, non-TSL docs, unrelated Skills, and all other unmanaged target-branch paths.
|
||||
- Do not special-case `docs/tsl/syntax`; the source tree and builder determine bundle contents.
|
||||
- Do not merge source and target branches.
|
||||
- Keep `GIT_ASKPASS`; never embed credentials in the repository URL.
|
||||
- Build outside the repository before checking out the target branch.
|
||||
- Do not modify TSL content, the builder, or either Skill.
|
||||
- Do not execute a TSL interpreter.
|
||||
|
||||
---
|
||||
|
||||
### Task 1: Enforce exact workflow ownership
|
||||
|
||||
**Files:**
|
||||
- Modify: `test/test_build_tsl_playbook.py`
|
||||
- Modify: `.gitea/workflows/sync-tsl-playbook.yml`
|
||||
|
||||
**Interfaces:**
|
||||
- Consumes: bundle paths `AGENTS.md`, `docs/tsl`, `skills/tsl-syntax-reference`, and `skills/tsl-api-reference`.
|
||||
- Produces: one `managed_paths` Bash array used by validation, removal, copy, and `git add -A`.
|
||||
- Preserves: every target-branch path absent from `managed_paths`.
|
||||
|
||||
- [ ] **Step 1: Write the failing static contract assertions**
|
||||
|
||||
Replace `test_sync_workflow_does_not_remove_entire_target_branch` with:
|
||||
|
||||
```python
|
||||
def test_sync_workflow_does_not_remove_entire_target_branch(self):
|
||||
text = SYNC_WORKFLOW.read_text(encoding="utf-8")
|
||||
|
||||
self.assertNotRegex(text, r"git rm -rf --quiet\s+\.")
|
||||
self.assertIn("managed_paths=(", text)
|
||||
for path in (
|
||||
"AGENTS.md",
|
||||
"docs/tsl",
|
||||
"skills/tsl-syntax-reference",
|
||||
"skills/tsl-api-reference",
|
||||
):
|
||||
self.assertIn(f'"{path}"', text)
|
||||
self.assertNotIn("generated_paths=(AGENTS.md docs skills)", text)
|
||||
self.assertIn('rm -rf -- "${managed_paths[@]}"', text)
|
||||
self.assertIn('git add -A -- "${managed_paths[@]}"', text)
|
||||
self.assertNotIn('cp -R "$bundle"/. "$REPO_DIR"/', text)
|
||||
self.assertNotIn(".gitea/ci/", text)
|
||||
self.assertNotIn("https://oauth2", text)
|
||||
self.assertNotIn("oauth2:${TOKEN}", text)
|
||||
self.assertNotRegex(text, r"REPO_URL=.*(TOKEN|WORKFLOW)")
|
||||
self.assertNotIn("git remote set-url", text)
|
||||
self.assertIn("GIT_ASKPASS", text)
|
||||
self.assertIn('REPO_URL="${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}.git"', text)
|
||||
```
|
||||
|
||||
- [ ] **Step 2: Extend the existing-target integration fixture**
|
||||
|
||||
In `test_sync_preserves_files_outside_generated_paths`, replace the standalone README setup with:
|
||||
|
||||
```python
|
||||
unmanaged_files = {
|
||||
"README.md": "manual branch note\n",
|
||||
"docs/python/index.md": "manual python docs\n",
|
||||
"skills/manual-skill/SKILL.md": "manual skill\n",
|
||||
}
|
||||
for relative, content in unmanaged_files.items():
|
||||
path = repo / relative
|
||||
path.parent.mkdir(parents=True, exist_ok=True)
|
||||
path.write_text(content, encoding="utf-8", newline="\n")
|
||||
|
||||
stale_managed_files = (
|
||||
"docs/tsl/stale.md",
|
||||
"skills/tsl-syntax-reference/stale.md",
|
||||
"skills/tsl-api-reference/stale.md",
|
||||
)
|
||||
for relative in stale_managed_files:
|
||||
path = repo / relative
|
||||
path.parent.mkdir(parents=True, exist_ok=True)
|
||||
path.write_text("stale\n", encoding="utf-8", newline="\n")
|
||||
|
||||
git(repo, "add", ".")
|
||||
git(repo, "commit", "-m", "manual target branch content")
|
||||
git(repo, "push", "-u", "origin", "tsl-playbook")
|
||||
```
|
||||
|
||||
- [ ] **Step 3: Assert preservation, cleanup, and idempotence**
|
||||
|
||||
After the first `run_sync(repo)`, add:
|
||||
|
||||
```python
|
||||
for relative, expected in unmanaged_files.items():
|
||||
result = run(
|
||||
["git", "show", f"HEAD:{relative}"],
|
||||
cwd=repo,
|
||||
check=False,
|
||||
)
|
||||
self.assertEqual(result.returncode, 0, msg=result.stderr)
|
||||
self.assertEqual(result.stdout, expected)
|
||||
|
||||
for relative in stale_managed_files:
|
||||
result = run(
|
||||
["git", "cat-file", "-e", f"HEAD:{relative}"],
|
||||
cwd=repo,
|
||||
check=False,
|
||||
)
|
||||
self.assertNotEqual(result.returncode, 0, msg=relative)
|
||||
|
||||
first_publish = git(repo, "rev-parse", "HEAD").stdout.strip()
|
||||
git(repo, "checkout", "main")
|
||||
run_sync(repo)
|
||||
second_publish = git(repo, "rev-parse", "HEAD").stdout.strip()
|
||||
self.assertEqual(second_publish, first_publish)
|
||||
```
|
||||
|
||||
Keep the existing assertions that generated `AGENTS.md`, `docs/tsl/index.md`, and both TSL Skill entrypoints exist.
|
||||
|
||||
- [ ] **Step 4: Run the focused suite and verify RED**
|
||||
|
||||
```powershell
|
||||
python -B -m unittest discover -s test -p 'test_build_tsl_playbook.py' -v
|
||||
```
|
||||
|
||||
Expected: FAIL because the current workflow has no `managed_paths` array and deletes the whole target `docs` and `skills` directories.
|
||||
|
||||
- [ ] **Step 5: Define the exact managed paths in the workflow**
|
||||
|
||||
Replace `generated_paths=(AGENTS.md docs skills)` with:
|
||||
|
||||
```bash
|
||||
managed_paths=(
|
||||
"AGENTS.md"
|
||||
"docs/tsl"
|
||||
"skills/tsl-syntax-reference"
|
||||
"skills/tsl-api-reference"
|
||||
)
|
||||
```
|
||||
|
||||
Use it for pre-checkout validation:
|
||||
|
||||
```bash
|
||||
for path in "${managed_paths[@]}"; do
|
||||
if [ ! -e "$bundle/$path" ]; then
|
||||
echo "ERROR: bundle is missing expected path: $path" >&2
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
```
|
||||
|
||||
- [ ] **Step 6: Implement exact cleanup, copying, and staging**
|
||||
|
||||
Replace the broad removal, bundle-root copy, and staging commands with:
|
||||
|
||||
```bash
|
||||
rm -rf -- "${managed_paths[@]}"
|
||||
for path in "${managed_paths[@]}"; do
|
||||
mkdir -p "$(dirname "$path")"
|
||||
cp -R -- "$bundle/$path" "$path"
|
||||
done
|
||||
|
||||
git add -A -- "${managed_paths[@]}"
|
||||
```
|
||||
|
||||
Do not add a `docs/tsl/syntax` condition or any other path exception.
|
||||
|
||||
- [ ] **Step 7: Run the focused suite and verify GREEN**
|
||||
|
||||
```powershell
|
||||
python -B -m unittest discover -s test -p 'test_build_tsl_playbook.py' -v
|
||||
```
|
||||
|
||||
Expected: all 7 tests pass. The integration test proves unmanaged root/docs/skills files survive, stale managed files disappear, and a second identical publication creates no commit.
|
||||
|
||||
- [ ] **Step 8: Inspect and commit the implementation**
|
||||
|
||||
```powershell
|
||||
git diff --check
|
||||
git diff -- .gitea/workflows/sync-tsl-playbook.yml test/test_build_tsl_playbook.py
|
||||
git add .gitea/workflows/sync-tsl-playbook.yml test/test_build_tsl_playbook.py
|
||||
git commit -m ':wrench: chore(ci): scope tsl playbook sync paths'
|
||||
```
|
||||
|
||||
Expected: the commit contains only the workflow and its regression tests.
|
||||
|
||||
---
|
||||
|
||||
### Task 2: Final workflow verification
|
||||
|
||||
**Files:**
|
||||
- Verify: `.gitea/workflows/sync-tsl-playbook.yml`
|
||||
- Verify: `test/test_build_tsl_playbook.py`
|
||||
|
||||
**Interfaces:**
|
||||
- Consumes: Task 1's exact managed-path publication flow.
|
||||
- Produces: fresh evidence that the workflow is scoped, secure, idempotent, and compatible with new target branches.
|
||||
|
||||
- [ ] **Step 1: Run the full focused suite in a clean process**
|
||||
|
||||
```powershell
|
||||
python -B -m unittest discover -s test -p 'test_build_tsl_playbook.py' -v
|
||||
```
|
||||
|
||||
Expected: 7 tests pass with `OK` and no errors or failures.
|
||||
|
||||
- [ ] **Step 2: Verify the ownership contract directly**
|
||||
|
||||
```powershell
|
||||
rg -n 'managed_paths|generated_paths|rm -rf|cp -R|git add -A' .gitea/workflows/sync-tsl-playbook.yml
|
||||
```
|
||||
|
||||
Expected: one four-entry `managed_paths` definition; no broad `generated_paths`; removal, copy, and staging derive from `managed_paths`; no whole-bundle copy.
|
||||
|
||||
- [ ] **Step 3: Verify repository state**
|
||||
|
||||
```powershell
|
||||
git status --short
|
||||
git show --stat --oneline HEAD
|
||||
```
|
||||
|
||||
Expected: the implementation commit contains only `.gitea/workflows/sync-tsl-playbook.yml` and `test/test_build_tsl_playbook.py`, with a clean working tree.
|
||||
|
||||
- [ ] **Step 4: Avoid a verification-only commit**
|
||||
|
||||
If verification passes without corrections, stop. If it exposes a defect, return to Task 1's RED → GREEN cycle and commit only the correction:
|
||||
|
||||
```powershell
|
||||
git commit -m ':bug: fix(ci): close tsl playbook sync verification gap'
|
||||
```
|
||||
@@ -1,194 +0,0 @@
|
||||
# TSL Syntax Reference 文档逻辑强化设计
|
||||
|
||||
## 背景
|
||||
|
||||
`tsl-syntax-reference` 已经形成清晰的单入口结构,但当前 `--query` 会直接返回整段正文。自然语言查询容易召回大节、输出数万字节,并把用户查询原样混入 Markdown。语法资料还会展示 API 调用,现有边界没有充分区分“语法外形示例”和“API 可用性事实”。
|
||||
|
||||
本设计选择破坏式升级,不保留旧查询输出协议。目标是让 Skill 默认采用“紧凑检索候选 → 精确取回章节”的两阶段流程,并把事实边界、自然语言映射和维护校验收紧为可测试契约。
|
||||
|
||||
## 目标
|
||||
|
||||
- `--query` 默认只输出紧凑候选,不再输出章节正文。
|
||||
- `--section` 是唯一正文取回入口。
|
||||
- 用户查询永远作为不可信数据转义,不得改变输出结构。
|
||||
- 改善中文口语、同义词、短 ASCII 关键字和混合查询的召回质量。
|
||||
- 索引 H2、H3、H4,使具体反例可精确取回。
|
||||
- Section ID 对符号标题稳定且无静默冲突。
|
||||
- 明确语法示例中的 API 调用不拥有 API 签名、环境 scope 或可用性事实。
|
||||
- 概念地图只表达自然语言职责,不输出链接、代码或可照写语法。
|
||||
- `--check` 覆盖每页职责、标题层级、Section ID、代码块身份和地图完整性。
|
||||
- 用 Python 和文档测试验证全部改动,不运行 TSL。
|
||||
- Skill 包不包含 `agents/openai.yaml` 等平台专用适配文件;通用发现元数据只写在 `SKILL.md` frontmatter。
|
||||
|
||||
## 非目标
|
||||
|
||||
- 不验证 TSL 示例能否编译或运行。
|
||||
- 不修改正在并发维护的 `tsl-api-reference` 资料、索引或 API 分类。
|
||||
- 不重写 24 篇参考资料的全部技术内容。
|
||||
- 不维持旧版 `--query` 的正文输出兼容性。
|
||||
- 不新增第三方搜索、分词或向量数据库依赖。
|
||||
|
||||
## 用户流程
|
||||
|
||||
### 自然语言起手
|
||||
|
||||
1. 用户需求尚未映射到 TSL 概念时,运行 `lookup.py --map`。
|
||||
2. 使用用户原始术语、错误文本和地图提示词执行 `lookup.py --query ... --mode ...`。
|
||||
3. 查询结果只列出候选的 Section ID、来源页、标题路径、分数、命中理由和短摘要。
|
||||
4. 选择支持当前结论的候选后,用 `lookup.py --section <ID>` 取正文。
|
||||
5. 正文不足、版本不明、需要 API 或项目事实时停止并交接,不从其他语言或模型记忆补全。
|
||||
|
||||
### 混合意图
|
||||
|
||||
收到同时包含语法、API、命名、工具链或运行环境的请求时,先拆分事实所有者:
|
||||
|
||||
- 语法外形、文件模型、表达式、控制流、对象和 TS-SQL 结构:本 Skill。
|
||||
- API 名称、签名、参数、返回值、平台 scope 与金融语义:`tsl-api-reference`。
|
||||
- 命名和风格:目标项目文档。
|
||||
- 执行命令、解释器和环境:最近的 `AGENTS.md`、项目脚本或 CI。
|
||||
|
||||
询问“如何运行”与实际执行 TSL 一样,必须读取最近的项目指引。不同所有者给出的事实不能直接拼接成“可运行”结论;API scope 与目标解释器兼容性缺失时必须停止。
|
||||
|
||||
## CLI 输出契约
|
||||
|
||||
### `--query`
|
||||
|
||||
输出固定结构:
|
||||
|
||||
```text
|
||||
# TSL Syntax Candidates
|
||||
|
||||
Mode: `write`
|
||||
Query: "已转义的单行 JSON 字符串"
|
||||
|
||||
## Candidate 1
|
||||
Score: 80
|
||||
Section ID: `...`
|
||||
Source: `references/05_functions_and_calls.md`
|
||||
Heading: `可直接照写示例 > 基础函数 / 过程骨架`
|
||||
Why: `标题命中:函数;代码词命中:function`
|
||||
Summary: 一行纯文本摘要。
|
||||
```
|
||||
|
||||
约束:
|
||||
|
||||
- 不包含正文、代码围栏或绝对文件路径。
|
||||
- Query 使用 JSON 字符串编码,换行和控制字符不可生成 Markdown 结构。
|
||||
- 默认最多五个候选,仍允许 `--limit 1..10`。
|
||||
- 没有候选时 stderr 输出明确缺口并返回 2。
|
||||
- `write` 模式把两个必要前置章节作为候选置顶并标记 `Required: yes`,不展开正文。
|
||||
|
||||
### `--section`
|
||||
|
||||
- 返回一个完整章节正文及逻辑来源路径。
|
||||
- 不接受 `--mode`。
|
||||
- 找不到时返回 2 并列出最相近的 Section ID。
|
||||
|
||||
### `--map`
|
||||
|
||||
- 每个参考页恰好一条职责摘要。
|
||||
- 摘要必须是纯文本,不含 Markdown 链接、行内代码、代码围栏或可直接执行的完整语法。
|
||||
- 地图只帮助选择查询概念,不能作为生成代码的事实来源。
|
||||
|
||||
## 检索设计
|
||||
|
||||
### 分词与规范化
|
||||
|
||||
- 继续使用 Unicode NFKC 和大小写折叠。
|
||||
- ASCII 标识符按完整 token 匹配,不使用任意子串匹配;`if` 不得命中 `TIniFile` 或 `ifCache`。
|
||||
- 中文保留连续词和二元词,但过滤高频口语停用词。
|
||||
- 维护一个小型、显式、可测试的领域同义词表,例如:
|
||||
- `打印`、`打出来` → `输出`、`writeLn`
|
||||
- `左连接` → `左联接`、`left join`
|
||||
- `列表` → `数组`
|
||||
- `复用文件` → `.tsf`、`unit`
|
||||
- `性能瓶颈` → `计时`、`profiler`
|
||||
- 同义词只用于召回,不作为 TSL 事实输出。
|
||||
|
||||
### 索引粒度
|
||||
|
||||
- 建立 H2/H3/H4 层级栈,不再只索引 H2/H3。
|
||||
- H2/H3 聚合节仍可被检索,但候选优先选择更深、更具体的命中。
|
||||
- 同一页默认最多保留两个候选,避免五个名额被同一专题占满。
|
||||
- 标题精确命中、代码标识符命中、页标题命中和正文命中分别计分;输出实际排序所依据的总分。
|
||||
|
||||
### Section ID
|
||||
|
||||
- 标题符号在 slug 前显式编码:`*`、`**`、`[]` 等得到不同稳定片段。
|
||||
- 同一页生成相同 ID 时,`--check` 直接失败,不再按出现顺序静默追加 `-2`。
|
||||
- ID 由相对页名和完整标题路径组成;来源只显示 `references/<page>.md`。
|
||||
|
||||
## 事实边界
|
||||
|
||||
参考页可以在语法示例中使用 API 名称作为占位或观察手段,但必须遵守:
|
||||
|
||||
- 示例只证明调用在该语法结构中的外形,不证明 API 的签名、返回值、平台 scope 或目标解释器可用性。
|
||||
- 需要依赖 API 结论时,答案必须重新查询 `tsl-api-reference`。
|
||||
- 参考页不得把 API 参数规格或平台分类声明为本 Skill 独占事实。
|
||||
- 输出、缓存、矩阵和运行时服务等现有交叉内容,通过 Skill 总边界和相关页面的局部说明统一澄清;本次不批量迁移 API 条目。
|
||||
|
||||
## SKILL.md 与发现元数据
|
||||
|
||||
- frontmatter 补充 `Tinysoft`、`天软`、`TS-SQL`、公式/策略脚本和常见语法报错触发词。
|
||||
- 不新增 OpenAI、Claude、Gemini 等平台专用元数据;各运行时直接读取通用 frontmatter。
|
||||
- 主流程只展示两阶段检索命令。
|
||||
- 增加安全传参要求:不得未经引用把用户文本拼进 shell。
|
||||
- 增加混合意图检查表和 API scope × 解释器兼容性阻断条件。
|
||||
- “执行 TSL 前读取项目指引”扩展为“询问或执行 TSL 运行方式时”。
|
||||
- 保留五种代码块身份,但明确“可直接照写”只描述源码外形,不等于目标环境验证通过。
|
||||
|
||||
## 校验设计
|
||||
|
||||
`--check` 新增以下失败条件:
|
||||
|
||||
- 参考页缺少唯一 H1。
|
||||
- 参考页缺少且仅缺少一个非空 `## 本篇职责`。
|
||||
- 概念地图页数与参考页数不一致。
|
||||
- Section ID 冲突。
|
||||
- 标题层级从 H2/H3/H4 非法跳级。
|
||||
- 地图摘要包含 Markdown 链接、代码围栏或为空。
|
||||
- 查询前置章节锚点缺失。
|
||||
|
||||
现有代码块身份、围栏闭合、本地链接和人工路由协议检查继续保留。
|
||||
|
||||
## 测试策略
|
||||
|
||||
严格采用 RED → GREEN → REFACTOR,不运行 TSL。
|
||||
|
||||
### 查询与安全
|
||||
|
||||
- 查询中的换行、Markdown 标题、围栏和控制字符不能改变输出结构。
|
||||
- `--query` 输出不含章节正文、代码围栏或绝对路径。
|
||||
- `--section` 仍返回完整正文。
|
||||
- `if` 只命中独立关键字,不命中 `TIniFile`/`ifCache`。
|
||||
- “数据库左连接”召回 TS-SQL;“打出来”召回基础输出/函数示例。
|
||||
|
||||
### 自然语言矩阵
|
||||
|
||||
- 固化当前 24 条专题矩阵。
|
||||
- 最低门槛:Top-1 不低于 20/24,Top-5 为 24/24。
|
||||
- 对五个当前漏召回专题建立独立回归测试。
|
||||
- 增加混合意图、英文、错别字和 API 交接场景。
|
||||
|
||||
### 结构与文档
|
||||
|
||||
- 每页职责、H4 索引、符号 slug、ID 冲突和地图纯文本都有失败测试。
|
||||
- frontmatter 覆盖中英文发现关键词。
|
||||
- SKILL.md 必须明确两阶段检索、安全传参、事实边界和无 TSL 验证要求。
|
||||
|
||||
## 迁移与发布
|
||||
|
||||
- 这是明确的破坏式 CLI 变更;仓库内测试、评测说明和调用示例同步更新。
|
||||
- 不增加兼容开关、旧输出模式或弃用期。
|
||||
- 构建与安装流程继续复制同一 Skill 目录,不增加运行时依赖。
|
||||
- 验证命令仅包含 `lookup.py --check`、Python 单元/结构测试和相关构建/安装测试。
|
||||
|
||||
## 验收标准
|
||||
|
||||
- 所有新增测试先观察到预期失败,再由最小实现修复。
|
||||
- 24 条自然语言矩阵达到 Top-1 ≥ 20、Top-5 = 24。
|
||||
- 注入复现用例不再产生伪造 Markdown 标题或围栏。
|
||||
- 默认查询输出不超过 8KB,且不包含正文代码块。
|
||||
- 24 个参考页全部进入纯文本概念地图。
|
||||
- 目标 Skill、测试和路由文档通过 Python/文档校验。
|
||||
- 全程不运行 TSL,不修改 `tsl-api-reference` 文档树。
|
||||
@@ -1,116 +0,0 @@
|
||||
# Sync TSL Playbook Workflow Upgrade Design
|
||||
|
||||
## Background
|
||||
|
||||
The `tsl-playbook` branch is a mixed branch: the synchronization workflow owns the
|
||||
generated TSL playbook paths, while README files and other manually maintained
|
||||
content must survive each publication.
|
||||
|
||||
The current workflow treats the whole `docs/` and `skills/` directories as generated:
|
||||
|
||||
```bash
|
||||
generated_paths=(AGENTS.md docs skills)
|
||||
```
|
||||
|
||||
That ownership boundary is wider than the bundle actually owns. TSL syntax is now
|
||||
provided by `tsl-syntax-reference`, while the branch may also contain unrelated
|
||||
documentation and Skills that the workflow must not delete.
|
||||
|
||||
## Goal
|
||||
|
||||
Redesign `.gitea/workflows/sync-tsl-playbook.yml` so it updates only the TSL
|
||||
playbook paths while preserving every unrelated target-branch file.
|
||||
|
||||
## Managed Paths
|
||||
|
||||
The workflow exclusively owns these four paths:
|
||||
|
||||
```text
|
||||
AGENTS.md
|
||||
docs/tsl/
|
||||
skills/tsl-syntax-reference/
|
||||
skills/tsl-api-reference/
|
||||
```
|
||||
|
||||
It must preserve all other paths, including:
|
||||
|
||||
- root files such as `README.md`;
|
||||
- non-TSL documentation under `docs/`;
|
||||
- unrelated Skills under `skills/`;
|
||||
- any other manually maintained target-branch content.
|
||||
|
||||
The workflow does not special-case `docs/tsl/syntax`. Whether that path exists is
|
||||
determined by the source tree and the bundle builder, not by publication logic.
|
||||
|
||||
## Publication Flow
|
||||
|
||||
1. Clone and check out the latest `origin/main` in a temporary repository.
|
||||
2. Build the TSL bundle outside the repository working tree.
|
||||
3. Validate that all four managed paths exist in the bundle.
|
||||
4. Check out the existing remote target branch, or create an orphan target branch.
|
||||
5. Remove only the four managed paths from the target working tree.
|
||||
6. Copy each managed path explicitly from the bundle.
|
||||
7. Stage changes only for the four managed paths.
|
||||
8. Exit successfully without a commit when the staged diff is empty.
|
||||
9. Otherwise commit with the source SHA footer and push the target branch.
|
||||
|
||||
Explicit copying is preferred over copying the bundle root. This keeps the workflow's
|
||||
write behavior aligned with its declared ownership boundary even if the bundle gains
|
||||
additional top-level files later.
|
||||
|
||||
## Branch Behavior
|
||||
|
||||
For an existing `tsl-playbook` branch, the workflow starts from
|
||||
`origin/tsl-playbook`, so unmanaged files retain their current content and history.
|
||||
|
||||
For a new target branch, the workflow creates an orphan branch and clears the inherited
|
||||
index before adding the four managed paths. Source-only files from `main` must not leak
|
||||
into the new branch.
|
||||
|
||||
The workflow must not merge the source and target branches. It publishes a generated
|
||||
commit directly on the target branch.
|
||||
|
||||
## Failure Handling and Security
|
||||
|
||||
- Missing bundle paths fail before the target branch is modified.
|
||||
- `set -euo pipefail` remains enabled for shell steps.
|
||||
- The bundle remains outside the repository so branch checkout cannot clobber it.
|
||||
- Authentication continues through `GIT_ASKPASS`; credentials are never embedded in
|
||||
the remote URL.
|
||||
- Temporary bundle and askpass files are removed through existing cleanup paths.
|
||||
- A failed copy or validation must prevent commit and push.
|
||||
|
||||
## Test Strategy
|
||||
|
||||
Update the workflow contract and integration tests before changing the workflow.
|
||||
|
||||
The tests must demonstrate that:
|
||||
|
||||
- the managed path list contains the four exact paths and not the broad `docs` or
|
||||
`skills` directories;
|
||||
- root files outside the managed set survive synchronization;
|
||||
- non-TSL documentation under `docs/` survives synchronization;
|
||||
- unrelated Skills under `skills/` survive synchronization;
|
||||
- stale files inside each managed directory are deleted;
|
||||
- a newly created target branch contains the bundle paths without source-branch
|
||||
leakage;
|
||||
- an unchanged bundle produces no commit;
|
||||
- token handling remains based on `GIT_ASKPASS` and a credential-free repository URL.
|
||||
|
||||
## Non-Goals
|
||||
|
||||
- Do not change the contents of `AGENTS.md`, TSL documentation, or either TSL Skill.
|
||||
- Do not add special handling for obsolete syntax-document paths.
|
||||
- Do not redesign `scripts/build_tsl_playbook.py` unless a workflow test exposes an
|
||||
interface mismatch required by the exact managed-path contract.
|
||||
- Do not delete or rewrite unrelated files on the target branch.
|
||||
- Do not change the target branch name or commit-message convention.
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- Publication changes are limited to the four managed paths.
|
||||
- Unmanaged target-branch files are byte-for-byte preserved.
|
||||
- Stale content inside managed paths is removed.
|
||||
- New target branches contain no accidental files inherited from `main`.
|
||||
- Existing workflow security assertions and sync integration tests pass.
|
||||
- The full `test_build_tsl_playbook.py` suite passes.
|
||||
Reference in New Issue
Block a user