文件
hyb-oa/.trellis/spec/hyb-oa/project-governance.md
T
wangsli e92596a2ac docs: 补充 worktree 生命周期约定(治理文档)
记录本次会话的教训:worktree 曾累积到16个、占用两位数GB磁盘,
因为清理被推迟。补充判断"何时该删"的具体方法
(git worktree list + merge-base --is-ancestor),供后续开发/agent参考。
2026-09-26 08:48:46 +08:00

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.md or implement.md for complex process, API, migration, permission, deployment or workflow-governance changes.
  • In sub-agent dispatch mode, curate implement.jsonl and check.jsonl with 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 --source in 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 with get_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 finish only to clear a session pointer when ending work; it does not complete or archive the task. Activate the intended task explicitly if current reports session-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 quick git worktree list + git merge-base --is-ancestor <head> main check 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 --source before 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:

  1. Continue toward configurable, scalable product completeness.
  2. 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/hooks
  • python3 ./.trellis/scripts/task.py validate <task>
  • node scripts/qa/check-trellis-governance.mjs
  • git diff --check
  • bash 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 --version and .trellis/.version.
  • Runtime check: python3 -m compileall -q .trellis/scripts .codex/hooks.
  • Project preview: trellis update --dry-run using the target CLI version.
  • Task smoke tests: task.py validate, task.py current --source --json and task.py list --json.

3. Contracts

  • Run compile checks with the same python3 executable 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 --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-<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.
  • .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; 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.json happened 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/scripts and .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 with git 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/*.csv files.
  • 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>/, writes task.json.status=completed and sets completedAt.

3. Contracts

  • For interrupted or superseded tasks, status=completed means lifecycle closed; it does not mean the original goal or every acceptance criterion was achieved.
  • Before archive, add a Lifecycle Closeout section to prd.md and a concise task.json.notes summary 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.json for status, completedAt and truthful notes.
  • Run node scripts/qa/check-trellis-governance.mjs and git 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.yaml sets codex.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.mjs and python3 ./.trellis/scripts/task.py validate <task>.

3. Contracts

  • Inline mode may keep both JSONL files at zero real entries; the seed _example row does not count as injected context.
  • If an inline task contains real entries, each entry must still be valid JSON with non-empty file and reason; 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 with Placeholder `_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 + _example with 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 validate reports 0 curated entries.
  • Sub-agent + seed-only manifest -> fail: task.py validate reports Placeholder `_example` row left by an older task.py create before the empty-manifest gate; governance reports *_seed and *_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 _example plus 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 validate for the active task.
  • Run cd server && npm test -- tests/unit/scripts/trellis-runtime-contract.test.js tests/unit/scripts/check-trellis-governance.test.js for 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