From 1c557e4926cce618feec6c70e4f85187260a7feb Mon Sep 17 00:00:00 2001 From: shunli Date: Sun, 2 Aug 2026 16:46:56 +0800 Subject: [PATCH] =?UTF-8?q?=E5=8D=87=E7=BA=A7=E6=9C=AC=E6=9C=BA=20Trellis?= =?UTF-8?q?=20=E8=87=B3=200.6.12=EF=BC=88upgrade-trellis-0-6-12=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .codex/agents/trellis-check.toml | 8 +- .codex/agents/trellis-implement.toml | 8 +- .codex/agents/trellis-research.toml | 17 +++-- .codex/hooks/inject-subagent-context.py | 8 ++ .trellis/.template-hashes.json | 14 ++-- .trellis/.version | 2 +- .trellis/config.yaml | 27 ++++--- .trellis/scripts/common/active_task.py | 7 +- .trellis/scripts/common/git.py | 11 ++- .trellis/scripts/common/session_context.py | 75 ++++++++++++++++--- .trellis/scripts/common/task_context.py | 8 +- .trellis/spec/hyb-oa/project-governance.md | 15 +++- .../08-02-upgrade-trellis-0-6-12/check.jsonl | 1 + .../08-02-upgrade-trellis-0-6-12/design.md | 40 ++++++++++ .../implement.jsonl | 1 + .../08-02-upgrade-trellis-0-6-12/implement.md | 41 ++++++++++ .../tasks/08-02-upgrade-trellis-0-6-12/prd.md | 54 +++++++++++++ .../08-02-upgrade-trellis-0-6-12/task.json | 26 +++++++ AGENTS.md | 22 ++++++ 19 files changed, 335 insertions(+), 50 deletions(-) create mode 100644 .trellis/tasks/08-02-upgrade-trellis-0-6-12/check.jsonl create mode 100644 .trellis/tasks/08-02-upgrade-trellis-0-6-12/design.md create mode 100644 .trellis/tasks/08-02-upgrade-trellis-0-6-12/implement.jsonl create mode 100644 .trellis/tasks/08-02-upgrade-trellis-0-6-12/implement.md create mode 100644 .trellis/tasks/08-02-upgrade-trellis-0-6-12/prd.md create mode 100644 .trellis/tasks/08-02-upgrade-trellis-0-6-12/task.json diff --git a/.codex/agents/trellis-check.toml b/.codex/agents/trellis-check.toml index 157e6ed9..50ceae33 100644 --- a/.codex/agents/trellis-check.toml +++ b/.codex/agents/trellis-check.toml @@ -17,9 +17,11 @@ CRITICAL — Recursion guard (read first): You are the Trellis reviewer agent. Trellis Context Loading Protocol: -- Look for the `` marker in your input above. -- If the marker is present, the native `SubagentStart` hook has already loaded the role-specific task artifacts and spec context. Proceed directly with the check work. -- If the marker is absent, find `Active task: ` in your dispatch prompt. Read `/check.jsonl`, each file listed there, `/prd.md`, `/design.md` if present, and `/implement.md` if present before checking. If the dispatch prompt has no active-task path, ask the main session; do not guess or use another session's task. +- First look for `Full hook output saved to: ` in your input above. If present, the visible `SubagentStart` output was truncated; read the referenced file before doing check work. +- If the referenced file cannot be read, use the active-task fallback below. +- If there is no saved-output notice and the `` marker is present, the hook loaded the complete role-specific task artifacts and spec context. +- If there is no saved-output notice and the marker is absent, use the active-task fallback below. +- For the fallback, find `Active task: ` in your dispatch prompt. Read `/check.jsonl`, each file listed there, `/prd.md`, `/design.md` if present, and `/implement.md` if present before checking. If the dispatch prompt has no active-task path, ask the main session; do not guess or use another session's task. Your job is to review code changes against specs AND fix issues directly — not just report them. You have write access; use it. diff --git a/.codex/agents/trellis-implement.toml b/.codex/agents/trellis-implement.toml index e25f4c56..d0cc3473 100644 --- a/.codex/agents/trellis-implement.toml +++ b/.codex/agents/trellis-implement.toml @@ -17,9 +17,11 @@ CRITICAL — Recursion guard (read first): You are the Trellis implementer agent. Trellis Context Loading Protocol: -- Look for the `` marker in your input above. -- If the marker is present, the native `SubagentStart` hook has already loaded the role-specific task artifacts and spec context. Proceed directly with the implementation work. -- If the marker is absent, find `Active task: ` in your dispatch prompt. Read `/implement.jsonl`, each file listed there, `/prd.md`, `/design.md` if present, and `/implement.md` if present before doing the work. If the dispatch prompt has no active-task path, ask the main session; do not guess or use another session's task. +- First look for `Full hook output saved to: ` in your input above. If present, the visible `SubagentStart` output was truncated; read the referenced file before doing implementation work. +- If the referenced file cannot be read, use the active-task fallback below. +- If there is no saved-output notice and the `` marker is present, the hook loaded the complete role-specific task artifacts and spec context. +- If there is no saved-output notice and the marker is absent, use the active-task fallback below. +- For the fallback, find `Active task: ` in your dispatch prompt. Read `/implement.jsonl`, each file listed there, `/prd.md`, `/design.md` if present, and `/implement.md` if present before doing the work. If the dispatch prompt has no active-task path, ask the main session; do not guess or use another session's task. Rules: - Read before write. Follow `.trellis/spec/` guidance relevant to the task. diff --git a/.codex/agents/trellis-research.toml b/.codex/agents/trellis-research.toml index cba82b8d..b0b2fef1 100644 --- a/.codex/agents/trellis-research.toml +++ b/.codex/agents/trellis-research.toml @@ -15,12 +15,17 @@ through the chat reply is a failure. ## Trellis Context Loading Protocol -Look for the `` marker in your input above. - -- If the marker is present, the native `SubagentStart` hook has supplied the - `Active task: ` header and research-only context. Use that task path. -- If the marker is absent, find `Active task: ` in your dispatch prompt. - If it is absent too, ask the main session for the path; do not run +- First look for `Full hook output saved to: ` in your input above. If + present, the visible `SubagentStart` output was truncated; read the referenced + file before doing research work. +- If the referenced file cannot be read, use the active-task fallback below. +- If there is no saved-output notice and the + `` marker is present, the hook supplied the + complete `Active task: ` header and research-only context. +- If there is no saved-output notice and the marker is absent, use the + active-task fallback below. +- For the fallback, find `Active task: ` in your dispatch prompt. If it + is absent too, ask the main session for the path; do not run `task.py current`, guess, or use another session's task. - Do not load `implement.jsonl` or `check.jsonl`; research is role-isolated. diff --git a/.codex/hooks/inject-subagent-context.py b/.codex/hooks/inject-subagent-context.py index 35590ea1..cfe7b15b 100755 --- a/.codex/hooks/inject-subagent-context.py +++ b/.codex/hooks/inject-subagent-context.py @@ -32,6 +32,14 @@ import sys from pathlib import Path from typing import Any +# Hook hosts send UTF-8 JSON regardless of the process locale. +_stdin_reconfigure = getattr(sys.stdin, "reconfigure", None) +if callable(_stdin_reconfigure): + try: + _stdin_reconfigure(encoding="utf-8", errors="replace") + except (OSError, ValueError): + pass + # IMPORTANT: Force stdout to use UTF-8 on Windows # This fixes UnicodeEncodeError when outputting non-ASCII characters if sys.platform.startswith("win"): diff --git a/.trellis/.template-hashes.json b/.trellis/.template-hashes.json index 1877fcde..093dc952 100644 --- a/.trellis/.template-hashes.json +++ b/.trellis/.template-hashes.json @@ -31,9 +31,9 @@ ".agents/skills/trellis-meta/references/platform-files/skills-and-commands.md": "e39831d860bd27a04e7757f7bd941ab83b3c5b11ad4460cec03975b655f26cc3", ".agents/skills/trellis-meta/SKILL.md": "208e92ace3b8d979d8158b3bd0169185f508f2e1424fcd78fcd03cbcc39516a1", ".agents/skills/trellis-start/SKILL.md": "79a5ba7a2aff3c72e06d7f4cd6942dc4f4f4092dd40f9c8e94f1838024a81e4d", - ".codex/agents/trellis-check.toml": "42d774d175dab732872e1056aa70005b6a1693d93fecfde6b9c86ecc50bd7b9e", - ".codex/agents/trellis-implement.toml": "c21eb1a671ebb8afe476ad54ae9ae1e33e4e97750bfdc4eabecc772a4d4e777b", - ".codex/agents/trellis-research.toml": "200b9fca30a68e027d2e8b2b4c785fafccb9aa75853e70abd92d2d07d7cba5d1", + ".codex/agents/trellis-check.toml": "79070c63fa404fc53061cca5194bf66db839132b67a57b9c8d6a295037ba7308", + ".codex/agents/trellis-implement.toml": "388fb8f39797e0ee6cf4db447c859c79b4ac15f531f7e1e3c68c1d1d71a1c188", + ".codex/agents/trellis-research.toml": "4435ce73197ba1d29d40359a3279b6423f7e4f559a449f934c016808090066c4", ".codex/hooks/session-start.py": "14de3be1cf6eb9c9feba348d8998b407f3837d6c0756b74210c9200543440677", ".codex/hooks/inject-workflow-state.py": "9ce43910ac39cbb0e4d1783fbde931761eb04536c7f82661a96d95ea72c14bde", ".codex/hooks.json": "85a58ba7cdf1e19e7f75ddcc64e5680180c487ca266a74bd5005f31abeee2e02", @@ -42,18 +42,18 @@ ".trellis/scripts/__init__.py": "1242be5b972094c2e141aecbe81a4efd478f6534e3d5e28306374e6a18fcf46c", ".trellis/scripts/add_session.py": "876dad478edf70db59acccaae9cb4db646a155681f730bd99af48de72ddc9881", ".trellis/scripts/common/__init__.py": "3d5e9347141f0296319a5beb29d69ae714c5a474b9078caeb3edd7c5f6562e22", - ".trellis/scripts/common/active_task.py": "3fcafde57bdc4442940081d1617295e0f4f8e620d0d4fbb97d33394e8679a39c", + ".trellis/scripts/common/active_task.py": "31271e3b69b5a5eca958d8ce25f61fe852eb6e48d32c150471da8e7272fd6119", ".trellis/scripts/common/cli_adapter.py": "5d6bd9d6f5c631e7e792db7dd343351317f9643bc87b73a9a98abd51cefb4307", ".trellis/scripts/common/config.py": "8d2e5f8ccfcd5f622cd2af002aa761f3d3ffcc653182fefb2268afd102e77bca", ".trellis/scripts/common/developer.py": "f5f833123abe68890171b4da825a324216d24913f6b5ad9245afc556424ffd7b", - ".trellis/scripts/common/git.py": "ea9ad7686bae598b04e41281ca938f83c06d8021716332947ea51339e7cb1aa9", + ".trellis/scripts/common/git.py": "6fc5845d0104dd506ebd8b366a24cb4b1e3d8777e4e6acc12ea15c9d8e2662f2", ".trellis/scripts/common/git_context.py": "fa30ced454f1a91ffc9f8b2abeb32225e3447cbdc90bad783797374eba07265d", ".trellis/scripts/common/io.py": "75648caae03d5b1107d7aeccaa785d133b25762266e54a520d90ca8c76b43bdb", ".trellis/scripts/common/log.py": "471df6895cfac80f995edebbf9974f6b7440634b7a688f28b8331c868bc0f3cf", ".trellis/scripts/common/packages_context.py": "efe158d7c99c2268851d0216fbb08de22836e418a8dbeb73575b8cc249eed7b7", ".trellis/scripts/common/paths.py": "05898ef136cc7c4d861b05fbf2b16d53ddd3e6f311a231d4fcfcb81bde7c45ee", ".trellis/scripts/common/safe_commit.py": "baa5c82324eb62154374ec63394ecdc8609bb37d93892e3bcb88f452bb7d6446", - ".trellis/scripts/common/session_context.py": "19927c62d3fd70fecf70da0c5b25c6c81e63683f8a594a1ae3b5d6c115070368", + ".trellis/scripts/common/session_context.py": "4ed3e13b2878ba367e9f2e2cd709b396f806152902df2cd1cd1478317d069017", ".trellis/scripts/common/task_context.py": "51301e180ce702c6a5b1a2dd1b155a6eed1bf2b05cb2ac1e51081212628b11d4", ".trellis/scripts/common/task_queue.py": "0be61f713462b1fe4574927c82fc4704e678afe72dcb9813543aedf2f9e9e0c5", ".trellis/scripts/common/task_store.py": "e3c2fbf8b79b591e39fc3c9f4e2f3ee0c840c8201c94a16709ec743fa45037f6", @@ -86,6 +86,6 @@ ".agents/skills/trellis-spec-bootstrap/references/spec-task-planning.md": "ef493d028c3b0807a8a534bb71fb92a68129f273db763ad27ceb464a522e799d", ".agents/skills/trellis-spec-bootstrap/references/spec-writing.md": "e9800fe9ed4a4cd87062ea1829cf2caa8d170ec15e141678a6a30e74c497f47d", ".agents/skills/trellis-spec-bootstrap/SKILL.md": "97bfa68c06cebb558eb4464bc1b81f7d2d56040d75baa8de1ee5ad90cca0196a", - ".codex/hooks/inject-subagent-context.py": "fe7e315b6ef5ca28dafa9a92072e4a07278492ee9cab74d42f0c0b6addf51662" + ".codex/hooks/inject-subagent-context.py": "abffa237eb53f87ae6ffa434063b46b03d58a36a84cdb8fe88bfc5f243aab609" } } \ No newline at end of file diff --git a/.trellis/.version b/.trellis/.version index 18640022..e9acb99e 100644 --- a/.trellis/.version +++ b/.trellis/.version @@ -1 +1 @@ -0.6.9 \ No newline at end of file +0.6.12 \ No newline at end of file diff --git a/.trellis/config.yaml b/.trellis/config.yaml index c9bc8b84..e06751b3 100644 --- a/.trellis/config.yaml +++ b/.trellis/config.yaml @@ -74,10 +74,15 @@ session_auto_commit: false #------------------------------------------------------------------------------- # Codex (dispatch behavior) #------------------------------------------------------------------------------- -# Codex-only knob; other platforms ignore it. Trellis 0.6.9 supports native -# sub-agent context injection, but hyb-oa deliberately keeps the main session -# in charge unless a task explicitly approves sub-agent work. "sub-agent" is -# retained upstream as a backwards-compatible alias for "auto". +# Codex-only knob; other platforms ignore it. Trellis 0.6.12 supports native +# sub-agent context injection and recovery of truncated hook output, but hyb-oa +# deliberately keeps the main session in charge unless a task explicitly +# approves sub-agent work. "sub-agent" remains a backwards-compatible alias +# for "auto"; invalid explicit values safely use inline mode. +# +# In "auto" mode, sub-agents inherit the main session model unless their +# generated `.codex/agents/trellis-*.toml` file pins a model. There is no +# config.yaml model override. # codex: dispatch_mode: inline # keep Codex main-session edits until a task explicitly asks for sub-agents @@ -116,9 +121,11 @@ channel: #------------------------------------------------------------------------------- # Sub-agent context injection limits #------------------------------------------------------------------------------- -# Trellis 0.6.9 caps injected task artifacts and referenced context. Defaults -# are sufficient for hyb-oa; keep them documented but implicit until a real -# task proves they need tuning. +# Trellis 0.6.12 caps injected task artifacts and referenced context. Oversized +# files are truncated with notice; total-budget overflow degrades remaining +# files to path/reason/size index lines. Defaults are sufficient for hyb-oa; +# keep them documented but implicit until a real task proves they need tuning. +# `0` disables the corresponding byte limit. # # context_injection: # max_file_bytes: 32768 @@ -128,8 +135,10 @@ channel: #------------------------------------------------------------------------------- # Per-turn prompt injection #------------------------------------------------------------------------------- -# Optional one-turn escape hatch. The project does not enable it by default so -# Trellis task state remains deterministic. +# Optional one-turn escape hatch for the per-turn `` breadcrumb. +# It uses a case-insensitive standalone-word match and does not affect session +# start or sub-agent context injection. The project keeps it disabled so task +# state remains deterministic. # # prompt_injection: # skip_keyword: "no-trellis" diff --git a/.trellis/scripts/common/active_task.py b/.trellis/scripts/common/active_task.py index c439fc7c..0eec6dfd 100755 --- a/.trellis/scripts/common/active_task.py +++ b/.trellis/scripts/common/active_task.py @@ -612,13 +612,16 @@ def clear_active_task( platform_input: dict[str, Any] | None = None, platform: str | None = None, ) -> ActiveTask: - """Clear the active task by deleting the current session context file.""" + """Clear the active task by deleting its resolved session context file.""" context_key = resolve_context_key(platform_input, platform) if not context_key: return ActiveTask(None, "none") previous = resolve_active_task(repo_root, platform_input, platform) - context_path = _context_path(repo_root, context_key) + if not previous.task_path or not previous.context_key: + return previous + + context_path = _context_path(repo_root, previous.context_key) if context_path.is_file(): _remove_file(context_path) return previous diff --git a/.trellis/scripts/common/git.py b/.trellis/scripts/common/git.py index ed00a9a2..426836ce 100755 --- a/.trellis/scripts/common/git.py +++ b/.trellis/scripts/common/git.py @@ -10,11 +10,17 @@ import subprocess from pathlib import Path -def run_git(args: list[str], cwd: Path | None = None) -> tuple[int, str, str]: +def run_git( + args: list[str], + cwd: Path | None = None, + timeout: float | None = None, +) -> tuple[int, str, str]: """Run a git command and return (returncode, stdout, stderr). Uses UTF-8 encoding with -c i18n.logOutputEncoding=UTF-8 to ensure - consistent output across all platforms (Windows, macOS, Linux). + consistent output across all platforms (Windows, macOS, Linux). Callers + may provide a timeout for best-effort probes; normal Git operations remain + unbounded by default. """ try: git_args = ["git", "-c", "i18n.logOutputEncoding=UTF-8"] + args @@ -25,6 +31,7 @@ def run_git(args: list[str], cwd: Path | None = None) -> tuple[int, str, str]: text=True, encoding="utf-8", errors="replace", + timeout=timeout, ) return result.returncode, result.stdout, result.stderr except Exception as e: diff --git a/.trellis/scripts/common/session_context.py b/.trellis/scripts/common/session_context.py index eb02369a..8f1fd1ce 100755 --- a/.trellis/scripts/common/session_context.py +++ b/.trellis/scripts/common/session_context.py @@ -17,6 +17,7 @@ import json import os import re import subprocess +import sys from pathlib import Path from .active_task import resolve_context_key @@ -64,11 +65,17 @@ _POLYREPO_IGNORED_DIRS = { "__pycache__", } _POLYREPO_SCAN_MAX_DEPTH = 2 +_POLYREPO_SCAN_MAX_REPOS = 8 +_GIT_PROBE_TIMEOUT_SECONDS = 2.0 def _is_git_worktree(path: Path) -> bool: """Return True when path is inside a Git worktree.""" - rc, out, _ = run_git(["rev-parse", "--is-inside-work-tree"], cwd=path) + rc, out, _ = run_git( + ["rev-parse", "--is-inside-work-tree"], + cwd=path, + timeout=_GIT_PROBE_TIMEOUT_SECONDS, + ) return rc == 0 and out.strip().lower() == "true" @@ -91,13 +98,27 @@ def _collect_git_repo_info(name: str, rel_path: str, repo_dir: Path) -> dict | N if not (repo_dir / ".git").exists(): return None - _, branch_out, _ = run_git(["branch", "--show-current"], cwd=repo_dir) + status_rc, status_out, _ = run_git( + ["status", "--porcelain"], + cwd=repo_dir, + timeout=_GIT_PROBE_TIMEOUT_SECONDS, + ) + if status_rc != 0: + return None + changes = len([line for line in status_out.splitlines() if line.strip()]) + + _, branch_out, _ = run_git( + ["branch", "--show-current"], + cwd=repo_dir, + timeout=_GIT_PROBE_TIMEOUT_SECONDS, + ) branch = branch_out.strip() or "unknown" - _, status_out, _ = run_git(["status", "--porcelain"], cwd=repo_dir) - changes = len([l for l in status_out.splitlines() if l.strip()]) - - _, log_out, _ = run_git(["log", "--oneline", "-5"], cwd=repo_dir) + _, log_out, _ = run_git( + ["log", "--oneline", "-5"], + cwd=repo_dir, + timeout=_GIT_PROBE_TIMEOUT_SECONDS, + ) return { "name": name, @@ -120,20 +141,36 @@ def _collect_root_git_info(repo_root: Path) -> dict: "recentCommits": [], } - _, branch_out, _ = run_git(["branch", "--show-current"], cwd=repo_root) + _, branch_out, _ = run_git( + ["branch", "--show-current"], + cwd=repo_root, + timeout=_GIT_PROBE_TIMEOUT_SECONDS, + ) branch = branch_out.strip() or "unknown" - _, status_out, _ = run_git(["status", "--porcelain"], cwd=repo_root) + status_rc, status_out, _ = run_git( + ["status", "--porcelain"], + cwd=repo_root, + timeout=_GIT_PROBE_TIMEOUT_SECONDS, + ) status_lines = [line for line in status_out.splitlines() if line.strip()] - _, short_out, _ = run_git(["status", "--short"], cwd=repo_root) + _, short_out, _ = run_git( + ["status", "--short"], + cwd=repo_root, + timeout=_GIT_PROBE_TIMEOUT_SECONDS, + ) - _, log_out, _ = run_git(["log", "--oneline", "-5"], cwd=repo_root) + _, log_out, _ = run_git( + ["log", "--oneline", "-5"], + cwd=repo_root, + timeout=_GIT_PROBE_TIMEOUT_SECONDS, + ) return { "isRepo": True, "branch": branch, - "isClean": len(status_lines) == 0, + "isClean": status_rc == 0 and len(status_lines) == 0, "uncommittedChanges": len(status_lines), "statusShort": short_out.splitlines(), "recentCommits": _parse_recent_commits(log_out), @@ -143,12 +180,16 @@ def _collect_root_git_info(repo_root: Path) -> dict: def _discover_child_git_repos(repo_root: Path) -> list[tuple[str, str]]: """Discover child Git repositories using the init-time polyrepo heuristic.""" found: list[str] = [] + overflow = False def is_candidate_dir(path: Path) -> bool: name = path.name return not name.startswith(".") and name not in _POLYREPO_IGNORED_DIRS def scan(rel_dir: Path, depth: int) -> None: + nonlocal overflow + if overflow: + return if depth >= _POLYREPO_SCAN_MAX_DEPTH: return abs_dir = repo_root / rel_dir @@ -165,11 +206,23 @@ def _discover_child_git_repos(repo_root: Path) -> list[tuple[str, str]]: rel_dir / child.name if rel_dir != Path(".") else Path(child.name) ) if (child / ".git").exists(): + if len(found) >= _POLYREPO_SCAN_MAX_REPOS: + overflow = True + return found.append(child_rel.as_posix()) continue scan(child_rel, depth + 1) scan(Path("."), 0) + if overflow: + print( + "warning: found more than " + f"{_POLYREPO_SCAN_MAX_REPOS} child Git repositories; " + "skipping automatic Git status collection. Configure explicit " + "packages entries with path and git: true in .trellis/config.yaml.", + file=sys.stderr, + ) + return [] if len(found) < 2: return [] return [(path.replace("/", "_"), path) for path in sorted(found)] diff --git a/.trellis/scripts/common/task_context.py b/.trellis/scripts/common/task_context.py index 7c4db163..1c7a126f 100755 --- a/.trellis/scripts/common/task_context.py +++ b/.trellis/scripts/common/task_context.py @@ -236,22 +236,22 @@ def _validate_jsonl(jsonl_file: Path, repo_root: Path, task_dir: Path | None = N if extension in _CODE_FILE_EXTENSIONS and not _is_exempt_from_code_file_warning( file_path, task_rel ): - warning = ( + warning_message = ( f"{file_name}:{line_num}: Warning: {file_path} looks like a code file — " "implement/check.jsonl should reference spec/research docs; " "agents read code themselves" ) - print(f" {colored(warning, Colors.YELLOW)}") + print(f" {colored(warning_message, Colors.YELLOW)}") if max_file_bytes: size = full_path.stat().st_size if size > max_file_bytes: - warning = ( + warning_message = ( f"{file_name}:{line_num}: Warning: {file_path} is {size} bytes, " f"exceeds context_injection.max_file_bytes ({max_file_bytes}); " "injection will truncate it" ) - print(f" {colored(warning, Colors.YELLOW)}") + print(f" {colored(warning_message, Colors.YELLOW)}") if errors == 0: print(f" {colored(f'{file_name}: ✓ ({real_entries} entries)', Colors.GREEN)}") diff --git a/.trellis/spec/hyb-oa/project-governance.md b/.trellis/spec/hyb-oa/project-governance.md index e7398016..f6ca1ddb 100644 --- a/.trellis/spec/hyb-oa/project-governance.md +++ b/.trellis/spec/hyb-oa/project-governance.md @@ -125,6 +125,11 @@ recorded in task `07-26-small-team-internal-oa-review`. `--force` to erase `workflow.md`, `config.yaml`, `.gitignore` or `AGENTS.md`. - Treat dry-run output and manifest cleanliness as evidence, but do not assume a dry-run is write-free; inspect `git status` before and after it. +- `trellis update` may copy ignored, local-only platform settings into the new + `.trellis/.backup-/`. Keep that exact backup only through the + merge/rollback window, then remove it before the sensitive-content gate once + Git and the pinned prior CLI provide the rollback path. Do not weaken the + scanner and do not blanket-delete older backups outside the task scope. ### 4. Validation & Error Matrix @@ -134,6 +139,9 @@ recorded in task `07-26-small-team-internal-oa-review`. discovery, and rerun the preview. - `.new` conflict files remain -> upgrade is incomplete; merge or discard each one deliberately. +- Sensitive scan reports a complete local path inside the newly created + upgrade backup -> resolve the exact backup from this run, remove it after the + merge is stable, and rerun the scan; an ignored directory is still scanned. - Post-upgrade dry-run changes manifest again -> investigate template hash drift before commit. - No active task directory after the last task is archived -> valid idle state; @@ -157,8 +165,11 @@ recorded in task `07-26-small-team-internal-oa-review`. - Exercise task validate/current/list and run Trellis governance. - Archive the final active task and assert Trellis governance still passes with zero active task directories. -- Confirm no `.new`, backup, bytecode or out-of-scope platform assets are - tracked; finish with `git diff --check`. +- Record pre-existing backup directory names before update; confirm no `.new`, + newly generated backup, bytecode or out-of-scope platform assets are tracked + or left for the sensitive scanner. Do not interpret a historical backup as + evidence that the current upgrade backup may remain. Finish with + `git diff --check`. ### 7. Wrong vs Correct diff --git a/.trellis/tasks/08-02-upgrade-trellis-0-6-12/check.jsonl b/.trellis/tasks/08-02-upgrade-trellis-0-6-12/check.jsonl new file mode 100644 index 00000000..9dd3234a --- /dev/null +++ b/.trellis/tasks/08-02-upgrade-trellis-0-6-12/check.jsonl @@ -0,0 +1 @@ +{"_example": "Fill with {\"file\": \"\", \"reason\": \"\"}. Put spec/research files only — no code paths. Run `python3 .trellis/scripts/get_context.py --mode packages` to list available specs. Delete this line once real entries are added."} diff --git a/.trellis/tasks/08-02-upgrade-trellis-0-6-12/design.md b/.trellis/tasks/08-02-upgrade-trellis-0-6-12/design.md new file mode 100644 index 00000000..afba0f8f --- /dev/null +++ b/.trellis/tasks/08-02-upgrade-trellis-0-6-12/design.md @@ -0,0 +1,40 @@ +# Trellis 0.6.12 升级设计 + +## 1. 升级边界 + +升级分为两个独立但必须一致的对象:npm 全局安装的 `@mindfoldhq/trellis` CLI,以及仓库中由 `trellis update` 管理的 `.trellis/`、`.codex/`、`.agents/` 和 `AGENTS.md` 模板。`@mindfoldhq/trellis-core` 由 CLI 包依赖并与 CLI 同版本发布,不单独在项目中新增依赖。 + +## 2. 冲突处理策略 + +先用 0.6.12 CLI dry-run 确认变更,再运行 `trellis update --create-new --migrate`。自动更新文件接受上游版本;本地修改文件按下表处理,禁止 `--force`: + +| 文件 | 决策 | +| --- | --- | +| `.trellis/scripts/common/task_context.py` | 合入上游 `warning` → `warning_message` 修复,保留其余本地行为 | +| `.trellis/config.yaml` | 保留 `session_auto_commit: false`、`codex.dispatch_mode: inline` 和项目注释,吸收 0.6.12 有价值的配置说明 | +| `.trellis/.gitignore` | 保留 `workspace/` 忽略;上游 sidecar 仅删除该项目规则,不接受 | +| `.trellis/workflow.md` | 保留文件头部 hyb-oa overlay;其余上游模板无实质差异时不重写 | +| `AGENTS.md` | 在既有项目规则之后加入受管 `TRELLIS:START/END` 指引块,不覆盖现有规则 | +| `trellis-channel` command reference | 仅空白差异,不制造功能性提交 | + +升级后删除所有 `.new` sidecar;保留 CLI 自动创建的忽略型备份,直到验证完成。模板 hash 只由 CLI 更新,不手工编辑。 + +## 3. 自动更新面 + +接受 0.6.12 对以下运行时的上游更新: + +- 活动任务清理使用已解析 session key,避免错误清除 fallback/歧义会话。 +- Git helper 增加可选超时;session context 对自动 polyrepo 探测设置仓库数量与 Git 探测时间边界。 +- Codex hook 独立按 UTF-8 解码输入。 +- Codex implement/check/research agent 在注入输出被截断时优先读取保存的完整 hook 输出。 + +## 4. 状态与兼容 + +- 更新期间不运行 `task.py archive`,不迁移或删除任何 task。 +- 当前升级 task 保持 planning,直到用户批准本设计后才 `task.py start`。 +- 报销 task 仍保持 `in_progress`;升级 task 完成后可恢复该任务为当前 session 指针,不改变其业务状态。 +- 若升级验证失败,先停止,不用 `--force` 修复;必要时把 CLI 降回 `0.6.9`,并从 CLI 生成的备份恢复本轮涉及的受管文件。 + +## 5. 安全与输出 + +升级不访问生产系统,不读取 Secret 或个人资料。命令输出和提交仅包含版本、固定文件名、PASS/FAIL 与公开的上游变更摘要,不记录完整本地路径。 diff --git a/.trellis/tasks/08-02-upgrade-trellis-0-6-12/implement.jsonl b/.trellis/tasks/08-02-upgrade-trellis-0-6-12/implement.jsonl new file mode 100644 index 00000000..9dd3234a --- /dev/null +++ b/.trellis/tasks/08-02-upgrade-trellis-0-6-12/implement.jsonl @@ -0,0 +1 @@ +{"_example": "Fill with {\"file\": \"\", \"reason\": \"\"}. Put spec/research files only — no code paths. Run `python3 .trellis/scripts/get_context.py --mode packages` to list available specs. Delete this line once real entries are added."} diff --git a/.trellis/tasks/08-02-upgrade-trellis-0-6-12/implement.md b/.trellis/tasks/08-02-upgrade-trellis-0-6-12/implement.md new file mode 100644 index 00000000..79d49eed --- /dev/null +++ b/.trellis/tasks/08-02-upgrade-trellis-0-6-12/implement.md @@ -0,0 +1,41 @@ +# Trellis 0.6.12 升级实施计划 + +## 1. 实施前门禁 + +- [x] 用户审查并批准 PRD、设计和本实施计划。 +- [x] 运行 `git status --short`,确认除当前 task 规划文件外没有未识别改动。 +- [x] 记录全局 CLI、项目版本和 npm 最新稳定版,拒绝预发布版本。 +- [x] 运行 0.6.12 `update --dry-run`,确认变更集合仍与规划一致;若新增覆盖范围则停止并重新审查。 + +## 2. 升级与合并 + +- [x] 用现有 npm 全局前缀安装 `@mindfoldhq/trellis@0.6.12`,确认 CLI 生效。 +- [x] 执行 `trellis update --create-new --migrate`,不使用 `--force`。 +- [x] 接受 7 个自动更新文件和 CLI 管理的版本/hash 更新。 +- [x] 逐项合并 `task_context.py`、`config.yaml`、`AGENTS.md`;明确保留 `.gitignore`、workflow overlay 和纯空白 command-reference 当前版本。 +- [x] 清除已决策的 `.new` sidecar,确认任务、spec、workspace 和开发者状态未被覆盖。 + +## 3. 验证 + +- [x] `trellis --version` 与 `.trellis/.version` 均为 `0.6.12`。 +- [x] `trellis update --dry-run` 无版本升级或 pending migration,仓库无 `.new` 文件。 +- [x] `python3 -m compileall -q .trellis/scripts .codex/hooks`。 +- [x] `python3 ./.trellis/scripts/task.py validate .trellis/tasks/08-02-upgrade-trellis-0-6-12`。 +- [x] `python3 ./.trellis/scripts/get_context.py`、`task.py current --source` 和活动任务列表正常。 +- [x] `node scripts/qa/check-trellis-governance.mjs`。 +- [x] `node scripts/qa/check-process-contract.mjs --baseline --json`。 +- [x] `node scripts/qa/check-sensitive-content.mjs` 与 `git diff --check`。 +- [x] 核对 `session_auto_commit: false`、`dispatch_mode: inline`、`workspace/` ignore、hyb-oa workflow overlay 与受管 AGENTS block。 + +## 4. 提交与恢复 + +- [ ] 按逻辑提交升级文件和任务证据,中文 commit message 引用 `upgrade-trellis-0-6-12`。 +- [ ] 不部署应用,不归档报销 task;升级 task 收口后恢复报销 task 的 session 当前指针。 +- [ ] 若验证失败且无法在既定范围内修复,停止并按设计回退 CLI 和受管文件,不留下半升级状态。 + +## 5. 实施证据(2026-08-02) + +- 全局 CLI、项目版本与 npm 稳定版均为 `0.6.12`;稳定 dry-run 仅报告 4 个明确保留的项目 overlay,没有升级、自动更新或 migration 待执行。 +- 7 个上游模板文件自动更新;`task_context.py` 与 AGENTS 受管块合入,config 的 inline/禁用自动提交和项目注释保留,workspace ignore 与 workflow overlay 未被覆盖,零 `.new` 残留。 +- 首轮治理门禁发现本轮升级备份复制了本机私有平台设置并触发完整路径敏感扫描;在 Git 与固定旧 CLI 已提供回滚路径后,仅删除本轮生成的精确备份目录并复跑,治理和敏感扫描转绿。未删除历史备份。 +- Python compileall、task validate/current/list、get_context、Trellis governance、process contract、敏感内容扫描和 `git diff --check` 全部 PASS;未访问生产系统或修改业务代码。 diff --git a/.trellis/tasks/08-02-upgrade-trellis-0-6-12/prd.md b/.trellis/tasks/08-02-upgrade-trellis-0-6-12/prd.md new file mode 100644 index 00000000..faa153e1 --- /dev/null +++ b/.trellis/tasks/08-02-upgrade-trellis-0-6-12/prd.md @@ -0,0 +1,54 @@ +# 升级本机 Trellis 工作流至 0.6.12 + +## Goal + +将本机全局 Trellis CLI 和 hyb-oa 项目内 Trellis 运行时从 `0.6.9` 升级到 npm 最新稳定版 `0.6.12`,获得 0.6.10–0.6.12 的上下文注入、会话清理、Git 探测和 UTF-8 修复,同时完整保留项目自己的 Trellis-first 治理规则。 + +## Background + +- 本机 `trellis --version` 与项目 `.trellis/.version` 当前均为 `0.6.9`;`@mindfoldhq/trellis` 和 `@mindfoldhq/trellis-core` 最新稳定版均为 `0.6.12`,两包按同一版本发布。 +- 使用 `0.6.12` CLI 执行只读 `trellis update --dry-run`,确认 7 个模板文件可自动更新,6 个本地定制文件需要人工决策;用户数据目录 `.trellis/tasks/`、`.trellis/spec/`、`.trellis/workspace/`受保护。 +- 0.6.10–0.6.12 主要修复 Python 3.9–3.11 兼容、Codex 截断的 `SubagentStart` 输出恢复、`task.py finish` 会话清理、UTF-8 hook 输入、Git 状态探测超时/数量边界,以及 Pi/channel 运行时问题。 + +## Scope + +- 本机 npm 全局安装的 `@mindfoldhq/trellis` CLI。 +- 仓库中由 Trellis 管理的 `.trellis/` 运行时、`.codex/` agent/hook、共享 Trellis skill 参考文件和 `AGENTS.md` 受管指引。 +- 当前项目的版本、模板 hash、配置兼容、活动任务解析和升级验证证据。 + +## Requirements + +- 同时升级全局 CLI 和项目生成文件,结束后两者版本必须一致为 `0.6.12`。 +- 禁止使用 `trellis update --force`;必须先 dry-run,再以 `.new` sidecar 方式处理本地冲突,并逐项人工合并。 +- 必须保留 hyb-oa 的 workflow overlay、中文提交要求、事实源优先级、生产审批边界和敏感信息约束。 +- 必须保留 `.trellis/config.yaml` 中 `session_auto_commit: false` 与 `codex.dispatch_mode: inline`。 +- 必须保留 `.trellis/.gitignore` 对个人 `workspace/` 日志的忽略,不得把个人日志纳入版本控制。 +- `.trellis/tasks/`、`.trellis/spec/`、`.trellis/workspace/`、开发者身份和活动任务状态不得丢失或被模板覆盖。 +- 上游可自动更新的运行时代码、Codex agent 和 hook 文件按 0.6.12 接收;本地修改的 `task_context.py` 合入 Python 兼容修复。 +- `AGENTS.md` 可加入 0.6.12 的受管 Trellis 指引块,但不得覆盖其现有 hyb-oa 规则;纯空白差异不制造无意义变更。 +- 升级仅作用于本机工具链和仓库 Trellis/Codex 集成,不修改业务代码、不部署、不触碰生产配置或真实数据。 + +## Acceptance Criteria + +- [x] `trellis --version`、项目 `.trellis/.version` 和 npm 稳定版均为 `0.6.12`。 +- [x] `trellis update --dry-run` 不再提示版本升级或待执行 migration;所有本地冲突均已明确保留或合并,仓库内没有遗留 `.new` 文件。 +- [x] 7 个自动更新文件包含 0.6.12 的会话清理、Git 探测、UTF-8 和 Codex 截断上下文恢复修复。 +- [x] `task_context.py` 保留现有行为并合入上游 `warning_message` 兼容修复。 +- [x] `session_auto_commit: false`、`codex.dispatch_mode: inline`、`workspace/` ignore 和 hyb-oa workflow overlay 均保持有效。 +- [x] 当前任务、其他活动任务、spec、workspace 和开发者身份仍可由 Trellis 脚本正常解析。 +- [x] Python 编译、task validate、上下文读取、Trellis governance、process contract、敏感内容扫描和 `git diff --check` 全部通过。 +- [ ] 变更以中文提交并引用 `upgrade-trellis-0-6-12`;不自动归档仍在运行的报销任务。 + +## Out of Scope + +- 不升级到 `0.7.0-beta` 或其他预发布版本。 +- 不切换到 channel-driven、TDD 或 sub-agent 自动派发工作流。 +- 不修改项目业务 spec、产品文档、应用代码、依赖或生产环境。 +- 不清理历史任务、个人 workspace 或其他 Trellis 运行证据。 + +## Test Plan + +- 比对全局 CLI、项目版本与 npm 稳定版,并复跑 `trellis update --dry-run`。 +- 编译 `.trellis/scripts` 与 Codex Python hooks,验证 task/context 生命周期命令可执行。 +- 运行当前 task validate、Trellis governance、process contract、敏感内容扫描和 `git diff --check`。 +- 逐项断言 `session_auto_commit: false`、`dispatch_mode: inline`、`workspace/` ignore、hyb-oa workflow overlay、AGENTS 受管块和零 `.new` 残留。 diff --git a/.trellis/tasks/08-02-upgrade-trellis-0-6-12/task.json b/.trellis/tasks/08-02-upgrade-trellis-0-6-12/task.json new file mode 100644 index 00000000..66b2661f --- /dev/null +++ b/.trellis/tasks/08-02-upgrade-trellis-0-6-12/task.json @@ -0,0 +1,26 @@ +{ + "id": "upgrade-trellis-0-6-12", + "name": "upgrade-trellis-0-6-12", + "title": "升级本机 Trellis 工作流至 0.6.12", + "description": "将本机 Trellis CLI 与 hyb-oa 项目工作流从 0.6.9 升级到最新稳定版 0.6.12,安全合并上游模板并保留项目定制。", + "status": "in_progress", + "dev_type": null, + "scope": "tooling", + "package": null, + "priority": "P2", + "creator": "shunli", + "assignee": "shunli", + "createdAt": "2026-08-02", + "completedAt": null, + "branch": null, + "base_branch": "main", + "worktree_path": null, + "commit": null, + "pr_url": null, + "subtasks": [], + "children": [], + "parent": null, + "relatedFiles": [], + "notes": "", + "meta": {} +} \ No newline at end of file diff --git a/AGENTS.md b/AGENTS.md index a9d1fb8f..9e3d35eb 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -53,3 +53,25 @@ - 涉及权限模型、状态机、workflow、数据迁移、审批规则时,实施后必须启动只读 review,不让 reviewer 改代码。 - 公网部署、生产配置、真实数据写入、数据库重置、企业微信写入和真实付款必须另行获得明确批准。 - 若 Trellis 规则与业务文档冲突:当前 Trellis task PRD/设计 > 当前模块规约 > 业务规约和 `docs/产品设计规则.md` 中的体验事实 > Trellis spec > 历史证据;前端视觉裁决优先使用 `frontend-design` skill,历史 Issue CSV 只用于追溯。 + + +# Trellis Instructions + +These instructions are for AI assistants working in this project. + +This project is managed by Trellis. The working knowledge you need lives under `.trellis/`: + +- `.trellis/workflow.md` — development phases, when to create tasks, skill routing +- `.trellis/spec/` — package- and layer-scoped coding guidelines (read before writing code in a given layer) +- `.trellis/workspace/` — per-developer journals and session traces +- `.trellis/tasks/` — active and archived tasks (PRDs, research, jsonl context) + +If a Trellis command is available on your platform (e.g. `/trellis:finish-work`, `/trellis:continue`), prefer it over manual steps. Not every platform exposes every command. + +If you're using Codex or another agent-capable tool, additional project-scoped helpers may live in: +- `.agents/skills/` — reusable Trellis skills +- `.codex/agents/` — optional custom subagents + +Managed by Trellis. Edits outside this block are preserved; edits inside may be overwritten by a future `trellis update`. + +