记录本次会话的教训:worktree 曾累积到16个、占用两位数GB磁盘, 因为清理被推迟。补充判断"何时该删"的具体方法 (git worktree list + merge-base --is-ancestor),供后续开发/agent参考。
24 KiB
Project Governance
Main Rule
Trellis task directories are the execution contract for new hyb-oa work.
docs/ remains the business fact source. Historical issues/*.csv files remain
audit evidence and optional export material, but they are not mandatory for new
tasks after TRELLIS2.
Required Flow
- Start non-trivial work from a Trellis task with a PRD.
- Put goal, scope, out-of-scope, acceptance and test plan in
prd.md. - Add
design.mdorimplement.mdfor complex process, API, migration, permission, deployment or workflow-governance changes. - In sub-agent dispatch mode, curate
implement.jsonlandcheck.jsonlwith stable spec or research context only. In Codex inline mode they may contain no real entries because the main session reads specs directly. - Give a concrete plan before non-trivial changes. Recorded approval of the same scope authorizes implementation and task-scoped validation; clerical backfill and repeated planning injection do not restart approval.
- Keep edits scoped to the current Trellis task.
- Commit only when explicitly requested, in Chinese with the task name or ID.
Task Reuse
- A task is an execution contract, not a ticket for every tool call or fix. Check current and directly related tasks first, not every historical task.
- Acceptance-related fixes stay in the current task. A different domain's fix goes to its suitable existing task as a scoped follow-up; link the blocker from the discovering task instead of duplicating implementation plans.
- Create only when independently managed work has no suitable existing owner. Size, complexity, worktree isolation or a separate commit alone do not require a new task. Tiny standalone edits may stay untracked unless tracking is requested.
- Task ownership, physical isolation and commit boundaries are separate choices. Reuse does not authorize mixed diffs, scope creep, production changes or commits.
- Append only changed scope/acceptance/evidence, preserving historical results. New risk or business decisions still need approval; unchanged approved fixes do not. Do not bulk merge/archive tasks or reopen archived tasks to reduce counts.
Active Task And Worktree Alignment
- On tracked-work entry, task scope change or execution worktree change, run
python3 .trellis/scripts/task.py current --sourcein the actual execution root. Match the returned task to the intended work. Reuse unchanged context; this is not a requirement to reload all specs or run checks every turn. - For an already approved task, correct a mismatch using official
task.py start <task>, then read back current and phase withget_context.py --mode phase --platform codex. Announce the task and phase when switching. Starting a task does not replace plan or risk approval. - Current task is session-scoped in
.trellis/.runtime/sessions/, not the legacy.trellis/.current-task. Do not write runtime JSON or shared pointers manually. A missing legacy pointer is not evidence of runtime failure. If identity is unavailable, report degraded mode and retain explicit task context; do not claim the session pointer was repaired from exit code alone. - Previously injected workflow-state can be stale after switching tasks. Use official current/phase output for this turn; distinguish that verification from actual next-turn platform injection. Do not enable hooks or change user configuration merely to hide an execution mismatch.
- Run validation in the worktree whose files changed. Record its baseline, task and evidence location. Isolated edits are not main-checkout integration; do not copy unrelated changes or activate unrelated tasks to tidy a status.
- Inline mode changes who implements/checks, not the task contract. Keep PRD/design/implementation, targeted check and spec/status update steps. Static governance checks cannot prove that the correct task was used or replace business acceptance and independent review.
- At stage handoff, record implementation, deployment, acceptance and pending
owner/action separately. Use official
task.py finishonly to clear a session pointer when ending work; it does not complete or archive the task. Activate the intended task explicitly if current reportssession-fallback; official resolution can infer the sole remaining session after a pointer is cleared. Do not treat that inferred task as new execution approval. Activate the next approved task explicitly when work actually resumes. Commit, push, archive and production operations retain their separate approval boundaries. - Worktrees are for genuine isolation (risky merges, DB-touching regression
runs, parallel tasks across sessions/agents), not a default habit. Once the
isolated work is merged or its branch's HEAD is an ancestor of the target
branch, remove the worktree (
git worktree remove) instead of leaving it "for later" — this project has previously accumulated over a dozen stale worktrees consuming double-digit GB of disk because cleanup was deferred. A quickgit worktree list+git merge-base --is-ancestor <head> maincheck identifies safe-to-remove ones during any disk or repo-hygiene pass.
Risk-Scoped Execution
- Pure consultation and small isolated edits do not need a task-creation question. Non-trivial work uses the task contract; new scope, risk or acceptance decisions require approval, not every intermediate action.
- Read the current phase and affected-layer contracts on demand. Reuse unchanged context; do not require every document or thinking guide per turn.
- Resolve task identity with
task.py current --sourcebefore broad session dumps. A missing pointer does not authorize guessing another session's task or creating a duplicate; inspect directly related task contracts. - Use the package index and heading search for large specs, then read the applicable general rules and complete scenarios. Do not treat a truncated search excerpt as full contract coverage. Keep skills as small entry points, with one owner for phase routing and reusable lessons.
- Prefer an available native plan tool. If unavailable, keep the checklist in the current task implementation notes rather than stopping or creating another planning system.
- Select checks from the task diff and its risks, not all unrelated dirty files. Reuse passing checks with unchanged inputs; report pre-existing failures without modifying unrelated code.
- Preserve privacy, permission/payment controls and separately approved production operations. Rules-only work does not require public deployment.
- Report local validation, independent review and public availability separately. If independent review is unavailable, leave that gate pending rather than claiming main-session tests fulfilled it.
- A finished stage may leave its parent task active. Never auto-commit, auto-archive, clean unrelated tasks or create duplicate retrospectives.
Solo Developer Operating Mode
hyb-oa is developed and maintained by one primary developer. Optimize every feature, improvement and refactor for low error rate, fast review, clear rollback and future self-maintenance.
- Prefer small, independently verifiable slices over broad rewrites.
- Before adding a new capability, ask whether it is high-frequency for a domestic tech startup with fewer than 10 people; low-frequency capability should be deferred, hidden behind advanced/admin entry, or kept as a manual procedure unless the current Trellis task explicitly approves it.
- Prefer existing modules, shared components, scripts and runbooks over new abstractions. Add platform-like infrastructure only when it removes real repeated work or reduces operational risk.
- Every task should state the minimum useful validation command set before implementation; do not rely on manual memory for gates.
- Keep rollout and rollback obvious. For public-facing changes, record the exact commit, deployment command, public check and whether the user can test.
- Capture reusable lessons in
docs/,.trellis/spec/or the current task while they are fresh; do not depend on chat history for project rules.
Design Decision: Internal-Only Small-Team Boundary
Context: hyb-oa serves one domestic technology startup with 5–10 people. It is a long-term internal management system, not a SaaS, commercial product or general-purpose OA platform.
Options Considered:
- Continue toward configurable, scalable product completeness.
- Optimize for a single company, overlapping roles and low maintenance.
Decision: choose the second option. Do not add multi-tenancy, commercial configuration, department rollout infrastructure, generic low-code/BPM, microservices, provider breadth or competitive feature parity unless a later Trellis task proves a concrete internal need. Prefer fixed workflows, role-aware navigation, existing providers, manual SOPs for low-frequency work and small independently removable modules.
Security and recovery do not become optional: authentication, least privilege, payment maker-checker, sensitive attachments, audit, off-host backup, restore drills and leaver access revocation remain required. Existing low-value modules are hidden or frozen first; data-bearing code is removed only through a scoped task with usage, export, retention and rollback evidence.
Decision gate:
clear owner + recurring internal use + material failure cost + no cheaper
reliable substitute -> consider a small Trellis task
otherwise -> defer, hide, freeze or keep as a manual SOP
Related: the evidence-backed baseline and initial retirement candidates are
recorded in task 07-26-small-team-internal-oa-review.
Design Decision: Finance-Backoffice Scope And Focused Adoption
Context: the 2026-08-11 production read-only census found extremely low team adoption across the system. This does not prove that missing features are the cause; several workflows intentionally live in Enterprise WeChat or manual processes.
Decision: separate product scope from adoption intensity. hyb-oa is the company's finance and low-frequency ledger backoffice, not its general daily collaboration suite. Reimbursement, invoices/OCR, approval, payment and AP are OA-primary workflows and should be used for real work. Contract, procurement and asset ledgers remain available but frozen until real events occur. HR, attendance and daily collaboration stay Enterprise-WeChat-primary. Project is currently a frozen zero-use domain and must not be disabled when that would degrade an OA-primary finance flow.
Payroll, performance, ERP/integration, budget and cutover capabilities are retirement-review candidates, not approved removals. Authentication, least privilege, payment maker-checker, audit, backup/restore and leaver revocation remain mandatory regardless of frequency.
Do not implement technical coercion to manufacture adoption. Requiring the next reimbursement to use OA is a management rule; notification expansion, onboarding guides, channel blocking or reminder machinery need observed user friction before they can become product work. The old R63-R66 expansion schedule is superseded.
OA-primary finance flow + observed blocker -> consider one small product task
low-frequency ledger + no real event -> keep available and frozen
Enterprise-WeChat-primary or cheaper reliable substitute -> do not duplicate
outside the focused scope -> hide, freeze or start a separate retirement review
Related: task 08-11-internal-oa-usage-census-and-simplification contains
the evidence boundary and module decision matrix. Task
08-11-internal-oa-finance-backoffice-positioning-and-navigation-reduction
records the adopted scope and first reversible navigation reduction.
Stop Conditions
- Production config, deployment, database reset, migration drift, real data writes, Enterprise WeChat writes, payment actions, or destructive cleanup need explicit approval.
- Permission model, status machine, workflow engine, migration or approval-rule changes require read-only reviewer review after implementation.
Current Local Commands
python3 -m compileall -q .trellis/scripts .codex/hookspython3 ./.trellis/scripts/task.py validate <task>node scripts/qa/check-trellis-governance.mjsgit diff --checkbash scripts/check-minimal.sh
Scenario: Trellis Project Upgrade Compatibility
1. Scope / Trigger
- Trigger: upgrading the project Trellis version, applying migrations, or refreshing managed platform assets.
- Purpose: prevent a template update from silently breaking the project's actual Python runtime or enabling an AI platform that is not in scope.
2. Signatures
- Version check:
trellis --versionand.trellis/.version. - Runtime check:
python3 -m compileall -q .trellis/scripts .codex/hooks. - Project preview:
trellis update --dry-runusing the target CLI version. - Task smoke tests:
task.py validate,task.py current --source --jsonandtask.py list --json.
3. Contracts
- Run compile checks with the same
python3executable used by project Trellis commands; compatibility with a newer developer interpreter is not sufficient. - Project-enabled platforms are authoritative. A local-only settings directory
such as
.claude/must not cause an out-of-scope managed platform bundle to enter the upgrade diff. - Temporarily isolating a local-only platform directory is allowed only when its exact contents are restored after update and the final diff confirms no managed assets for that platform were added.
- Preserve intentional local overlays through explicit merge; do not use
--forceto eraseworkflow.md,config.yaml,.gitignoreorAGENTS.md. - Treat dry-run output and manifest cleanliness as evidence, but do not assume
a dry-run is write-free; inspect
git statusbefore and after it. trellis updatemay copy ignored, local-only platform settings into the new.trellis/.backup-<timestamp>/. 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
- Runtime compile failure -> stop the upgrade and fix or revert the incompatible managed script before running task commands.
- Unexpected platform assets in dry-run or final diff -> stop, correct platform discovery, and rerun the preview.
.newconflict 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; governance still checks core files, docs registration and sensitive content.
- Only documented project overlays remain modified -> acceptable after task and governance gates pass.
5. Good/Base/Bad Cases
- Good: isolate a local-only
.claude/, update Codex assets, restore the local setting, compile with project Python, then verify a stable dry-run. - Base: update a project with no local platform ambiguity and run the same compile, task and governance checks.
- Bad: accept generated Claude assets because
.claude/settings.local.jsonhappened to exist, or validate Python code only with a newer interpreter.
6. Tests Required
- Assert CLI and project versions match the intended target.
- Compile every Python file under
.trellis/scriptsand.codex/hooks. - 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 in a disposable synthetic project only. This runtime check does not authorize archiving a real task.
- 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 withgit diff --check.
7. Wrong vs Correct
Wrong
trellis update reports success -> commit immediately
Correct
preview platform scope -> conservative update/migrate -> merge overlays
-> compile with project Python -> smoke-test task runtime -> stable dry-run
-> governance and project gates -> commit
Legacy Issue CSV
- Do not delete, bulk rewrite or move historical
issues/*.csvfiles. - New work may mention an old Issue CSV for traceability, but must not require a new CSV unless the user explicitly requests legacy/export evidence.
Scenario: Interrupted Or Superseded Task Closeout
1. Scope / Trigger
- Trigger: a Trellis task is interrupted, expired, cancelled or superseded and should leave the active-task list even though its original goal was not met.
- Purpose: keep lifecycle state clean without turning archival metadata into a false claim that acceptance criteria or business outcomes were completed.
2. Signatures
- Command:
python3 ./.trellis/scripts/task.py archive <task-name>. - Effects: moves the task under
.trellis/tasks/archive/<year-month>/, writestask.json.status=completedand setscompletedAt.
3. Contracts
- For interrupted or superseded tasks,
status=completedmeans lifecycle closed; it does not mean the original goal or every acceptance criterion was achieved. - Before archive, add a
Lifecycle Closeoutsection toprd.mdand a concisetask.json.notessummary covering: what was actually completed, what is missing, why the task stopped and which future task or condition replaces it. - Leave unmet acceptance checkboxes unchecked. Never create missing dates, tests, human confirmation or production evidence merely to make the archived task look complete.
- Current indexes or runbooks must use the same closeout semantics when the task previously represented an active operational fact.
4. Validation & Error Matrix
- All acceptance criteria achieved with evidence -> normal completed archive.
- Goal incomplete but closeout section and notes are explicit -> lifecycle archive is allowed; downstream readers must not infer goal completion.
- Goal incomplete and closeout evidence is absent -> do not archive yet.
- Historical evidence missing -> record it as missing; do not reconstruct or backdate evidence.
5. Good/Base/Bad Cases
- Good: a seven-day trial with only Day 1-Day 4 records is archived with the missing days, absent human closure and successor trial stated explicitly.
- Base: a fully verified implementation is archived with its implementation and deployment commits referenced.
- Bad: mark Day 5-Day 7 complete or check all PRD boxes only because the task is old and should disappear from the active list.
6. Tests Required
python3 ./.trellis/scripts/task.py validate <task-or-archive-path>.- Inspect archived
task.jsonforstatus,completedAtand truthfulnotes. - Run
node scripts/qa/check-trellis-governance.mjsandgit diff --check. - When the task affects current operational truth, run the documentation audit and verify index/runbook wording matches the closeout outcome.
7. Wrong vs Correct
Wrong
{ "status": "completed", "notes": "7 天试运行完成" }
Correct
{
"status": "completed",
"notes": "生命周期关闭:仅完成 Day 1-Day 4,原目标未完成;未来由新任务从 Day 1 替代。"
}
Scenario: Codex Inline Context Manifests
1. Scope / Trigger
- Trigger: creating, validating or finishing a Trellis task while
.trellis/config.yamlsetscodex.dispatch_mode: inline.
2. Signatures
- Config:
codex.dispatch_mode: inline | sub-agent. - Context files:
<task>/implement.jsonl,<task>/check.jsonl. - Gates:
node scripts/qa/check-trellis-governance.mjsandpython3 ./.trellis/scripts/task.py validate <task>.
3. Contracts
- Inline mode may keep both JSONL files at zero real entries; the seed
_examplerow does not count as injected context. - If an inline task contains real entries, each entry must still be valid JSON
with non-empty
fileandreason; normal path and context warnings apply. - Sub-agent mode requires at least one real entry in each manifest.
- Switching dispatch mode changes the validation policy, not historical task evidence and not the requirement to read specs before implementation.
- Trellis 0.6.16 start and validate share get_codex_dispatch_mode. Inline tolerates old seed-only rows; real entries require non-empty file/reason and resolved paths contained within the project. Sub-agent mode retains the curated-entry gate. The start override alone does not change validate.
- The inline seed exception applies only to an object whose sole key is
_example. Any additional key, including a falsey file/path, reason, type or an unknown key, makes it a mixed placeholder that validate must reject withPlaceholder `_example` row left by an older task.py create. Field absence is not equivalent to an explicitly empty or invalid value. - Update task.py and task_context.py as a coherent set: the newer start imports curated_entry_count. Keeping the old conflict file breaks imports.
- The fixed 0.6.16 upstream hooks do not enforce this marker contract: session-start counts truthy file fields; inject-subagent-context accepts truthy file/path fields. They can count or inject mixed marker rows that validate rejects. Hook readiness/injection is not manifest validation. Preserve the reviewed upstream hooks; do not treat their success as a substitute for the validator gate.
4. Validation & Error Matrix
- Inline + zero real entries -> pass with
*_inline_optional. - Inline + malformed real entry -> fail.
- Either mode +
_examplewith any additional key -> fail at that row; a valid file/reason pair does not make a mixed placeholder acceptable. - Sub-agent + empty manifest -> fail: governance reports
*_empty;task.py validatereports0 curated entries. - Sub-agent + seed-only manifest -> fail:
task.py validatereportsPlaceholder `_example` row left by an older task.py createbefore the empty-manifest gate; governance reports*_seedand*_empty. - Either mode + non-spec/non-research context -> warning for review.
5. Good/Base/Bad Cases
- Good: inline task uses the seed-only files and the main agent reads specs.
- Base: inline task keeps curated entries for audit; they remain validated.
- Bad: governance fabricates placeholder entries only to satisfy a rule meant for sub-agent context injection.
6. Tests Required
- Fixture test for inline seed-only pass.
- Fixture test for sub-agent seed-only failure.
- Negative fixtures for
_exampleplus empty/false/null file or path, reason/type/unknown keys and valid file/reason, in inline/sub-agent/auto. Assert exit 1 and exact placeholder diagnostics for both manifests. - Assert pure inline seeds count as zero entries and cannot hide a malformed following row; run new cases against the old condition to prove they fail.
- Run both Trellis governance and
task.py validatefor the active task. - Run
cd server && npm test -- tests/unit/scripts/trellis-runtime-contract.test.js tests/unit/scripts/check-trellis-governance.test.jsfor mode gates, real hook routing, path containment and explicit closeout boundaries.
7. Wrong vs Correct
Wrong
dispatch_mode=inline -> require at least one injected JSONL entry
Correct
dispatch_mode=inline -> allow zero real entries; validate any real entries
dispatch_mode=sub-agent -> require curated real entries