From 11d135f569eb27bbaf78430d50dcce890fb90b5e Mon Sep 17 00:00:00 2001 From: C1oud <120496180@qq.com> Date: Sun, 31 May 2026 19:34:26 +0800 Subject: [PATCH] feat: add daily progress mode --- codex/skills/threadsmith/SKILL.md | 69 ++- .../references/action-contracts.md | 61 ++- .../threadsmith/references/role-contracts.md | 10 + .../references/runtime-contract.md | 3 + .../threadsmith-daily-progress-mode-v1.md | 510 ++++++++++++++++++ .../full-governance-audit.json | 3 + .../human-centered-output/light-repair.json | 3 + .../unsafe-legacy-fallback.json | 3 + .../value-heartbeat.json | 3 + .../what-next-normal.json | 3 + .../runtime/src/humanCenteredOutput.test.ts | 3 + packages/runtime/src/nextBestStep.test.ts | 124 ++++- packages/runtime/src/nextBestStep.ts | 91 +++- packages/runtime/src/nextBestStepModel.ts | 12 + scripts/verify-threadsmith-skill-contract.mjs | 13 +- 15 files changed, 865 insertions(+), 46 deletions(-) create mode 100644 docs/plans/threadsmith-daily-progress-mode-v1.md diff --git a/codex/skills/threadsmith/SKILL.md b/codex/skills/threadsmith/SKILL.md index b2d9dcd..7c14d71 100644 --- a/codex/skills/threadsmith/SKILL.md +++ b/codex/skills/threadsmith/SKILL.md @@ -104,9 +104,11 @@ Examples: existing consumer, and close out once - single-role drive: the user asks specifically for reviewer only, verifier only, or "do not implement" -- stop-gate fallback: adding a CLI command, API endpoint, UI route, provider +- stop-gate fallback: adding a user-public CLI / API / UI route, provider default, public sync, tag, publish, migration, delete, or reset must pause for - operator review + operator review. Developer-only local CLI / docs / harness helpers may stay in + the work session when local, reversible, clearly non-public, and inside the + accepted scope. ### Work-Session Truth Writeback @@ -314,23 +316,65 @@ Definitions: Use the smallest closeout tier that still preserves orientation and safety: -- `lite`: small or low-risk work. Required fields: changed, verification, truth - (`updated`, `unchanged`, or `skipped with reason`), next, and optional risk. -- `standard`: normal bounded implementation. Required fields: result, changed - capability, verification, truth, remaining risk, and next phase. +- `daily-progress`: ordinary `light-repair` or `normal-implementation` work + inside approved scope. Use the compact Progress Card. +- `lite`: small or low-risk work. Required fields: changed, verification, + next, and optional risk. +- `standard`: normal bounded implementation. Prefer Progress Card unless a real + boundary needs the full skeleton. - `audit`: release, PR / merge, public docs, destructive operations, architecture boundaries, provider routing, security, or cross-agent state. Use the full Threadsmith Output Contract skeleton. -Use boundary full output for `recover`, bootstrap, audit closeout, accepted -phases, phase-boundary reports, and any response that changes durable truth in a -way that affects route, scope, acceptance, release, or cross-agent state. +Use boundary full output for `recover`, bootstrap, audit closeout, release / +PR / merge / destructive / provider / public behavior / cross-agent boundaries, +and any response that changes durable truth in a way that affects route, scope, +acceptance, release, or cross-agent state. + +### Daily Progress Card + +Use Daily Progress when no hard safety / recover / audit gate is active and the +work is inside an approved scope. + +```text +本轮完成: +- 新增/修复了什么能力。 + +关键改动: +- 只列关键代码、文档、测试或命令。 + +验证: +- 跑了什么,结果是什么。 +- 没跑什么,有什么风险。 + +记录状态: +- updated / unchanged / skipped only when durable writeback changed or omission would confuse the operator. + +下一步: +- 建议做什么。 +- 为什么现在做它。 +- 什么情况需要停下来问你。 + +需要你决定: +- 只有真正改变路线、边界、验收、真实数据、provider、发布或破坏性操作时才出现。 +``` + +Do not show `Threadsmith Decision` in daily progress output. + +Output selection precedence: + +1. recover / audit gates and hard stop conditions +2. explicit full audit request, or direct explanation without execution +3. explicit compact answer request only when no hard gate is active +4. daily progress eligibility +5. ordinary sync / drive / continuous output defaults ## Closeout Output Gate Before writing the final response, check whether the current action crossed a -phase or slice boundary. If any two of these signals are present, the response -is a closeout or phase-boundary report and must use boundary full output: +phase or slice boundary. If any two of these signals are present, and Daily +Progress is not eligible, the response is a closeout or phase-boundary report +and must use boundary full output: - the work is described as completed, accepted, closed out, or ready for the next phase @@ -346,7 +390,8 @@ and "下一步建议...". Those sentences may appear inside the required full output fields, but they cannot replace the skeleton. Threadsmith output rules override the default concise Codex final style for -closeout and phase-boundary reports. +audit closeout and real phase-boundary reports. Daily Progress overrides the +full skeleton for ordinary approved work. Use internal progress output for routine role-chain handoffs inside an approved phase, such as executor -> reviewer, reviewer -> verifier, or verifier -> diff --git a/codex/skills/threadsmith/references/action-contracts.md b/codex/skills/threadsmith/references/action-contracts.md index 23791c2..0bbde1c 100644 --- a/codex/skills/threadsmith/references/action-contracts.md +++ b/codex/skills/threadsmith/references/action-contracts.md @@ -276,10 +276,12 @@ Use work-session continuation when: - truth can be written at the session boundary without hiding a blocker. Do not bundle work into a work session when the next action changes product -semantics, exposes a new UI route / API endpoint / CLI command / public -integration, changes provider defaults, requires credentials, publishes, +semantics, exposes a new user-public UI route / API endpoint / CLI command / +public integration, changes provider defaults, requires credentials, publishes, merges, tags, deletes, resets, migrates data, contradicts committed truth, or -repairs a failed verification whose path is uncertain. +repairs a failed verification whose path is uncertain. Developer-only local CLI +or docs helpers may remain bundled when they are reversible, non-public, and +inside the accepted scope. Short approvals such as "同意,请使用 Threadsmith 推进" may continue an accepted work session until a natural stop. If the user asks for one explicit role or one @@ -508,11 +510,49 @@ Boundary full output is required for: - `recover` - bootstrap -- closeout -- accepted phases -- phase-boundary reports +- audit closeout +- release / PR / merge / destructive / provider / public behavior / + cross-agent boundaries +- phase-boundary reports that are not eligible for Daily Progress - any response that writes or proposes durable truth changes +Daily Progress output is required for ordinary `light-repair` or +`normal-implementation` work inside approved scope when no hard safety / +recover / audit gate is active. + +Daily Progress Card shape: + +```text +本轮完成: +- ... + +关键改动: +- ... + +验证: +- ... + +记录状态: +- updated / unchanged / skipped only when useful. + +下一步: +- ... + +需要你决定: +- only when there is a real route, scope, acceptance, data, provider, release, + destructive, or product boundary decision. +``` + +Do not show `Threadsmith Decision` in Daily Progress output. + +Output selection precedence: + +1. recover / audit gates and hard stop conditions +2. explicit full audit request, or direct explanation without execution +3. explicit compact answer request only when no hard gate is active +4. daily progress eligibility +5. ordinary sync / drive / continuous output defaults + Internal progress output is enough for routine role-chain handoffs inside an approved phase: @@ -535,14 +575,15 @@ do not write `.threadsmith/`, and label the source layer for factual claims. ## Phase Narrative Rule -For closeout, accepted phases, or any response that introduces the next phase, -Threadsmith must produce a phase narrative, not only a protocol status list. +For audit closeout, major accepted phases, or any non-daily-progress response +that introduces the next phase, Threadsmith must produce a phase narrative, not +only a protocol status list. ### Closeout Output Gate Before finalizing the response, check whether the action crossed a durable -phase or slice boundary. If any two closeout signals are present, boundary full -output is mandatory: +phase or slice boundary. If any two closeout signals are present and Daily +Progress is not eligible, boundary full output is mandatory: - completed, accepted, closed out, or ready for next phase - commit, PR, merge, release, tag, truth writeback, packet update, or closeout diff --git a/codex/skills/threadsmith/references/role-contracts.md b/codex/skills/threadsmith/references/role-contracts.md index 39fbe01..8ec1cce 100644 --- a/codex/skills/threadsmith/references/role-contracts.md +++ b/codex/skills/threadsmith/references/role-contracts.md @@ -184,6 +184,16 @@ Writeback tier guard: - `committed-truth`: update acceptance/status/supervision/phase history only when durable project state changed. +Daily role packet sparsening: + +- do not rewrite planner / executor / reviewer / verifier / closeout packets for + ordinary Progress Cards. +- refresh full role packets only for handoff, recovery, full-governance, + cross-agent work, release / public docs, major architecture decisions, or + explicit operator request. +- if skipped packet facts are needed for the next turn, write current-context or + evidence-summary instead. + Forbidden writes: - new implementation scope diff --git a/codex/skills/threadsmith/references/runtime-contract.md b/codex/skills/threadsmith/references/runtime-contract.md index 0dc88cf..00ea07a 100644 --- a/codex/skills/threadsmith/references/runtime-contract.md +++ b/codex/skills/threadsmith/references/runtime-contract.md @@ -319,6 +319,9 @@ deterministic from state: - surface audience: `internal`, `developer`, `operator`, or `user_public` - work visibility: `internal`, `developer_visible`, `operator_visible`, or `user_visible` +- output shape: `progress-card`, `audit-skeleton`, or `compact` +- role packet policy: `skip-daily` or `refresh-durable` +- writeback status visibility: `omit`, `optional`, or `required` For heartbeat-compatible closeout evidence, record these fields when available: diff --git a/docs/plans/threadsmith-daily-progress-mode-v1.md b/docs/plans/threadsmith-daily-progress-mode-v1.md new file mode 100644 index 0000000..758eef9 --- /dev/null +++ b/docs/plans/threadsmith-daily-progress-mode-v1.md @@ -0,0 +1,510 @@ +# Threadsmith Daily Progress Mode v1 Implementation Plan + +Goal: make everyday Threadsmith use feel like a lightweight development +partnership instead of repeated audit closeouts, while preserving strict +governance for real boundaries. + +Scope: add a daily progress mode for small and medium work, define a compact +Progress Card output, support work bundles and rolling closeout, reduce routine +role packet writeback, and make value / engineering-depth checkpoints more +visible without hardcoding any single project. + +Non-goals: Asterlea-specific wording, removing audit mode, removing committed +truth, weakening release / provider / destructive / user-public gates, changing +global skill installation during implementation, or redesigning the frontend. + +Assumptions: + +- The baseline includes Human-Centered Governance v1 and Noise Reduction / + Operator Comfort v1. +- Full audit output remains necessary for release, merge, destructive actions, + provider defaults, cross-agent state, recovery, public behavior, and major + architecture boundaries. +- Daily progress mode should be generic. Project-specific value language comes + from AGENTS.md, project preferences, or project brief. +- The fix should reduce operator-facing ceremony and durable writeback noise, + not hide important evidence. + +Verification: + +- `npm run verify:skill-contract` +- `npm run test --workspace @threadsmith/domain` +- `npm run test --workspace @threadsmith/runtime` +- `npm run test --workspace @threadsmith/orchestrator` +- `npm run verify:project-truth` +- `git diff --check` +- `npm run verify:skill-sync` only after an intentional global skill sync + +## Problem Summary + +Threadsmith is now safer and more continuous than earlier versions, but the +default experience is still too audit-shaped for daily development. + +Observed failures: + +- small slices receive full closeout skeletons; +- related micro-actions become separate phases; +- phase boundaries create a stop feeling even after the operator authorized + continued work; +- `.threadsmith` writeback still touches too many files during ordinary work; +- role packets repeat the same facts across planner / executor / reviewer / + verifier / closeout; +- next-phase previews can create local inertia instead of asking whether the + next engineering depth is still highest value. + +The intended shift: + +```text +from: every accepted phase gets a full audit-style closeout +to: ordinary work gets a compact progress card; real boundaries get audit output +``` + +## Concepts + +### Daily Progress Mode + +Daily progress mode is the default operator-facing output mode for low-risk +`light-repair` and ordinary `normal-implementation` work. + +Use it when all are true: + +- the work is inside an already approved scope; +- no public/user-facing semantics changed; +- no provider defaults, credentials, release, destructive action, real user + data, or cross-agent state changed; +- verification is narrow or standard; +- the next action belongs to the same work bundle or a clearly approved + continuation. + +Do not use it when: + +- full-governance is selected; +- a PR / merge / release / tag is created; +- a cross-thread / cross-agent handoff is needed; +- recovery from stale or contradictory truth is active; +- the operator asks for an audit-style report; +- the next action requires a new route, scope, or product decision. + +CLI/docs distinction: + +- developer-only local CLI / docs / harness work may use Daily Progress when it + is local, reversible, clearly non-public, and inside the approved scope. +- user-public CLI / docs / API, compatibility behavior, published docs, or + public support promises must use audit output. + +Output selection precedence: + +1. recover / audit gates and hard stop conditions; +2. explicit operator request for full audit, or direct explanation without + execution; +3. explicit compact answer request, only when no hard safety / recover / audit + gate is active; +4. daily progress eligibility; +5. ordinary sync / drive / continuous output defaults. + +### Progress Card + +Default daily output should use this compact shape: + +```text +本轮完成: +- 新增/修复了什么能力。 + +关键改动: +- 只列关键代码、文档、测试或命令。 + +验证: +- 跑了什么,结果是什么。 +- 没跑什么,有什么风险。 + +记录状态: +- `updated` / `unchanged` / `skipped`,只用一行说明,且只在 writeback status 对理解有帮助时出现。 + +下一步: +- 建议做什么。 +- 为什么现在做它。 +- 什么情况需要停下来问你。 + +需要你决定: +- 只有真正改变路线、边界、验收、真实数据、provider、发布或破坏性操作时才出现。 +``` + +Rules: + +- Omit `需要你决定` when there is no real decision. +- Do not include `Threadsmith Decision` in daily progress output. +- Do not expose role / packet / truth vocabulary unless needed for clarity. +- Mention the writeback tier only when it explains why few files changed. +- If the work changed only internal foundations, say it is not user-visible yet. + +### Audit Output + +Use full audit output only for: + +- architecture boundary changes; +- user-public behavior, public API / CLI / docs, published docs, or + compatibility behavior; +- provider default, credential, release, merge, tag, destructive, or migration + work; +- real user data or privacy-sensitive behavior; +- cross-agent / cross-thread handoff; +- stale truth recovery, major failed verification recovery, or proposal + adoption; +- explicit operator request for a full report. + +Audit output may keep the human-first full skeleton from the current +Threadsmith contract. Daily progress mode should not use that skeleton. + +### Work Bundle + +A work bundle groups 2-4 related actions under one accepted direction. + +Examples: + +- report builder + sample CLI + focused test; +- small runtime helper + direct consumer update + narrow verification; +- docs clarification + contract check + one fixture update. + +Allowed when: + +- all actions affect the same subsystem and same accepted goal; +- no new user-public semantics, provider default, real user data, release, + destructive action, or cross-agent state is introduced; +- verification can remain narrow or standard; +- one progress card can explain the whole bundle without hiding risk. + +Not allowed when: + +- actions cross unrelated subsystems; +- one action requires operator decision; +- verification failure makes the repair path unclear; +- a new product / public / release / provider / destructive boundary appears. + +### Rolling Closeout + +Rolling closeout is a closeout shape for "keep going" sessions. It reports the +work just completed but does not turn the next internal continuation into a new +approval point. + +Allowed when: + +- the operator has explicitly authorized continuous progress; +- the next action remains inside the same work bundle or approved direction; +- no audit stop appears; +- verification evidence is sufficient for the completed sub-step. + +Budget: + +- rolling closeout may continue for at most one work bundle, or 2-4 related + actions, before emitting a lightweight value checkpoint; +- after that checkpoint, continue only if the next action is still inside the + same approved direction and no hard stop appears. + +Stop instead when: + +- the next action changes route, scope, product semantics, real data, + provider, release, destructive behavior, or public surface; +- verification failed and the repair path is not clear; +- truth is stale or contradictory; +- the next action would require a new plan rather than continuing the bundle. + +### Role Packet Sparsening + +Role packets should be durable collaboration artifacts, not routine duplicated +summaries. + +Default daily behavior: + +- do not rewrite planner / executor / reviewer / verifier / closeout packets + for ordinary progress cards; +- use current context or evidence summary for daily continuity when the next + turn needs durable context; +- final response and command output are useful evidence display, but are not + durable truth by themselves; +- mark role packets stale only when their role-relevant truth actually changed. + +When skipping role packets: + +- update current-context or evidence-summary when the skipped packet would + otherwise be needed for the next action; +- treat it as needed when the next step depends on this round's conclusion, + the next step continues the same bundle across turns, the work may continue + cross-thread or cross-day, or this round changed scope, verification, stop + condition, blocker, or route; +- for `evidence-only` work that does not affect the next step, it is acceptable + to leave no `.threadsmith` writeback; +- explain skipped role packets only when omission could confuse the operator, + not in every daily progress card. + +Generate or refresh full role packets only for: + +- handoff to another thread or agent; +- recovery from stale / contradictory context; +- full-governance audit boundary; +- cross-agent state bridge work; +- release / public documentation / major architecture decisions; +- explicit operator request. + +### Value and Engineering-Depth Checkpoint + +Threadsmith should prevent local engineering inertia without forcing constant +operator meetings. + +Trigger: + +- after 3-5 internal-only or governance-heavy accepted slices without a value + checkpoint; +- after repeated gap checks; +- after the same subsystem receives several consecutive internal-only bundles; +- when next-step recommendations keep deepening the same internal layer. + +Output: + +- use a short daily progress note, not an audit report; +- ask whether the next work should continue engineering depth or shift toward + product/user value; +- use project-configured value language from AGENTS.md/preferences/project + brief; +- if no project wording exists, use a neutral fallback question: "continue + engineering depth, or shift toward user-visible value, product experience, or + validation quality?" +- allow the operator to skip without resetting the whole workflow. + +Counter source: + +- prefer phase history or closeout metadata when available; +- otherwise use current packet / evidence summary metadata; +- do not rely on model memory as the only counter source. + +### Gap Check Budget + +Gap checks should produce a direct implementation decision. + +Rules: + +- a gap check must end with one of: implement now, stop for operator decision, + run focused verification, or recover stale truth; +- if it chooses `implement now`, it must name the next file, module, command, or + test-level action. It must not only say "continue optimization"; +- if it cannot produce a concrete next action, it was too early or too broad; +- do not chain another gap check unless new material evidence appeared; +- after a gap check chooses an implementation path, the next normal action + should be implementation. + +### Verification Tiers + +Verification should match risk: + +- docs-only: `git diff --check`; run markdown parse only if the repo already + has an available command; run `jq` when JSON changed; +- skill contract copy: `npm run verify:skill-contract`; +- runtime metadata / recommendation model: targeted runtime tests plus full + runtime workspace tests when model types changed; +- domain preferences/schema: domain tests; +- orchestrator / autopilot behavior: orchestrator tests; +- release / launcher / public sync: CI and release-specific checks. + +Threadsmith should explain why a verification level was chosen only when the +operator would otherwise be surprised. + +## Files + +- Modify: `codex/skills/threadsmith/SKILL.md` +- Modify: `codex/skills/threadsmith/references/action-contracts.md` +- Modify: `codex/skills/threadsmith/references/role-contracts.md` +- Modify: `codex/skills/threadsmith/references/runtime-contract.md` +- Modify: `scripts/verify-threadsmith-skill-contract.mjs` +- Modify: `packages/runtime/src/nextBestStepModel.ts` +- Modify: `packages/runtime/src/nextBestStep.ts` +- Modify: `packages/runtime/src/nextBestStep.test.ts` +- Modify: `packages/runtime/src/humanCenteredOutput.test.ts` +- Modify or create: `packages/runtime/fixtures/human-centered-output/*.json` +- Optional modify: runtime metadata model files if Daily Progress needs + explicit fields. Prefer existing project-status, phase history, current + packet, or evidence-summary metadata over a new persistent metadata file. +- Optional modify: `packages/orchestrator/src/*` only if rolling closeout or + command capability detection has an orchestrator representation. +- Optional modify: `packages/fs-bridge/src/*` only if role packet sparsening or + writeback tier enforcement must happen at fs-bridge write time. +- Optional create: `docs/reports/threadsmith-daily-progress-mode-v1-closeout.md` + only if this change needs durable audit evidence. Ordinary daily-mode work + should not create reports by default. + +## Implementation Steps + +### 1. Add Daily Progress Output Contract + +Add daily progress mode to the skill/action contracts and verifier. + +Required behavior: + +- daily progress output uses the compact Progress Card; +- audit output keeps the full human-first skeleton; +- `Threadsmith Decision` is not shown in daily progress output; +- `需要你决定` appears only for real decisions. +- daily progress output still includes next step and stop condition. +- writeback status line appears only when durable writeback changed or omission + could confuse the operator. + +Verification: + +- `npm run verify:skill-contract` +- fixtures covering daily card vs audit output selection. + +### 2. Add Work Bundle Rules + +Teach Threadsmith when related actions should remain one bundle instead of +becoming several tiny phases. + +Required behavior: + +- group 2-4 same-subsystem actions under one accepted direction; +- audit stops still interrupt bundles; +- gap checks and docs-only work can join a bundle only when they directly + support the same executable action. + +Verification: + +- runtime tests for work bundle continuation; +- runtime tests for audit stop breaking a work bundle. + +### 3. Add Rolling Closeout Budget + +Teach Threadsmith when to report progress and continue without turning the next +internal continuation into a new approval point. + +Required behavior: + +- rolling closeout continues only when the next step remains inside the bundle; +- rolling closeout stops after one bundle or 2-4 related actions for a + lightweight value checkpoint; +- audit stops still interrupt rolling closeout immediately. + +Verification: + +- runtime tests for rolling closeout continuation; +- runtime tests for rolling budget exhaustion producing value checkpoint. + +### 4. Harden Role Packet Sparsening + +Make role packet writes opt-in by durability. + +Required behavior: + +- daily progress does not rewrite five role packets; +- role packets refresh only for handoff, recovery, full-governance, cross-agent + work, release/public docs, major architecture decisions, or explicit request; +- if role packets are not refreshed, current-context or evidence-summary must + carry next-turn facts when they are needed; +- the final output explains skipped packets only when omission could confuse + the operator. + +Verification: + +- contract checks for role packet sparsening; +- fs-bridge tests only if writeback enforcement is implemented there. + +### 5. Strengthen Value / Engineering-Depth Checkpoint + +Make value checkpoint more visible after internal-only work, without hardcoding +Asterlea. + +Required behavior: + +- detect repeated internal-only or governance-heavy work from available + metadata; +- emit a short value checkpoint using project-configured wording; +- allow skip/continue without turning it into a full audit phase. + +Verification: + +- runtime fixture for repeated internal-only bundles triggering value checkpoint; +- preference / metadata tests if new fields are added. + +### 6. Tighten Gap Check Budget + +Require gap checks to produce implementation, stop, verification, or recovery. + +Required behavior: + +- gap check output must name the next executable action or stop condition; +- if the result is `implement now`, it must include file, module, command, or + test-level next action detail; +- no gap-check chaining unless new material evidence appears; +- after gap check selects a path, implementation should be next. + +Verification: + +- runtime tests for gap-check-to-implementation flow; +- contract checks preventing vague gap-check output. + +### 7. Align Verification Tiers With Daily Mode + +Document and test risk-based verification expectations. + +Required behavior: + +- docs-only and small contract changes do not imply full runtime tests; +- runtime model/schema changes still run relevant package tests; +- audit/release/public changes still run broader checks. + +Verification: + +- contract checks for verification tiers; +- no broad test runner changes unless existing code needs it. + +## Acceptance Criteria + +- Daily small/medium work defaults to Progress Card output, not full audit + skeleton. +- Daily Progress Card still contains next step and stop condition. +- Daily output does not show `Threadsmith Decision`. +- Full audit output still appears for real boundaries. +- Developer-only local CLI/docs can use Daily Progress; user-public CLI/docs + still use audit output. +- Work bundles can group related small actions without hiding scope or risk. +- Rolling closeout continues inside an approved bundle, stops at real gates, and + cannot exceed one bundle / 2-4 related actions without a value checkpoint. +- Daily work does not rewrite role packets by default. +- Skipping role packets still leaves current-context/evidence-summary when the + next turn needs durable context. +- Gap checks must end in implement / stop / verify / recover. +- Value checkpoint appears after repeated internal-only or governance-heavy + work and uses project-configured language. +- Verification guidance is risk-tiered and does not over-test docs-only work. +- Contract checks protect the new daily-vs-audit split. +- No Asterlea-specific wording is hardcoded into Threadsmith core. + +## Risks + +- Making daily output too light could hide evidence needed for recovery. +- Work bundles could accidentally group unrelated actions. +- Rolling closeout could feel like continuing without consent if the next step + is outside the approved direction. +- Role packet sparsening could weaken cross-agent handoff if no substitute + context is available. +- Daily mode could make the operator think no durable writeback happened even + when truth changed, or vice versa. + +Mitigations: + +- Keep full audit output for real boundaries. +- Use explicit work-bundle stop conditions. +- Preserve current-context/evidence summary when role packets are skipped. +- Require handoff/recovery/cross-agent work to refresh durable packets. +- Progress Card should include one compact writeback line only when writeback + status matters: `记录状态: updated`, `记录状态: unchanged`, or + `记录状态: skipped`. + +## Done When + +- Skill, action, role, and runtime contracts describe daily progress mode + consistently. +- Runtime tests cover daily card, work bundle continuation, rolling closeout + budget, audit stop, role packet sparsening, value checkpoint, and gap-check + budget behavior. +- Contract fixtures cover Progress Card shape, optional writeback status line, + and omitted `Threadsmith Decision` in daily output. +- Verification commands pass. +- The final response states whether global skill sync happened. It should not + happen until after merge unless explicitly requested. diff --git a/packages/runtime/fixtures/human-centered-output/full-governance-audit.json b/packages/runtime/fixtures/human-centered-output/full-governance-audit.json index d488145..02a4869 100644 --- a/packages/runtime/fixtures/human-centered-output/full-governance-audit.json +++ b/packages/runtime/fixtures/human-centered-output/full-governance-audit.json @@ -10,6 +10,9 @@ "writebackTier": "committed-truth", "verificationLevel": "release", "outputBudget": "audit", + "outputShape": "audit-skeleton", + "rolePacketPolicy": "refresh-durable", + "writebackStatusVisibility": "required", "surfaceAudience": "user_public", "workVisibility": "user_visible" }, diff --git a/packages/runtime/fixtures/human-centered-output/light-repair.json b/packages/runtime/fixtures/human-centered-output/light-repair.json index 1fdd578..d6b03bb 100644 --- a/packages/runtime/fixtures/human-centered-output/light-repair.json +++ b/packages/runtime/fixtures/human-centered-output/light-repair.json @@ -11,6 +11,9 @@ "writebackTier": "evidence-only", "verificationLevel": "narrow", "outputBudget": "lite", + "outputShape": "progress-card", + "rolePacketPolicy": "skip-daily", + "writebackStatusVisibility": "omit", "surfaceAudience": "internal", "workVisibility": "internal" }, diff --git a/packages/runtime/fixtures/human-centered-output/unsafe-legacy-fallback.json b/packages/runtime/fixtures/human-centered-output/unsafe-legacy-fallback.json index a3359d5..1202029 100644 --- a/packages/runtime/fixtures/human-centered-output/unsafe-legacy-fallback.json +++ b/packages/runtime/fixtures/human-centered-output/unsafe-legacy-fallback.json @@ -11,6 +11,9 @@ "writebackTier": "committed-truth", "verificationLevel": "release", "outputBudget": "audit", + "outputShape": "audit-skeleton", + "rolePacketPolicy": "refresh-durable", + "writebackStatusVisibility": "required", "surfaceAudience": "operator", "workVisibility": "operator_visible" }, diff --git a/packages/runtime/fixtures/human-centered-output/value-heartbeat.json b/packages/runtime/fixtures/human-centered-output/value-heartbeat.json index ebaa926..4f7830c 100644 --- a/packages/runtime/fixtures/human-centered-output/value-heartbeat.json +++ b/packages/runtime/fixtures/human-centered-output/value-heartbeat.json @@ -10,6 +10,9 @@ "writebackTier": "current-context", "verificationLevel": "standard", "outputBudget": "standard", + "outputShape": "progress-card", + "rolePacketPolicy": "skip-daily", + "writebackStatusVisibility": "optional", "surfaceAudience": "operator", "workVisibility": "operator_visible", "nextStepKind": "value-heartbeat" diff --git a/packages/runtime/fixtures/human-centered-output/what-next-normal.json b/packages/runtime/fixtures/human-centered-output/what-next-normal.json index 2d59fad..9b25d3a 100644 --- a/packages/runtime/fixtures/human-centered-output/what-next-normal.json +++ b/packages/runtime/fixtures/human-centered-output/what-next-normal.json @@ -7,6 +7,9 @@ "writebackTier": "current-context", "verificationLevel": "standard", "outputBudget": "standard", + "outputShape": "progress-card", + "rolePacketPolicy": "skip-daily", + "writebackStatusVisibility": "optional", "surfaceAudience": "internal", "workVisibility": "internal", "nextStepKind": "work-session-continue" diff --git a/packages/runtime/src/humanCenteredOutput.test.ts b/packages/runtime/src/humanCenteredOutput.test.ts index 4e3593b..1d4e1a3 100644 --- a/packages/runtime/src/humanCenteredOutput.test.ts +++ b/packages/runtime/src/humanCenteredOutput.test.ts @@ -15,6 +15,9 @@ interface Fixture { writebackTier: string; verificationLevel: string; outputBudget: string; + outputShape?: string; + rolePacketPolicy?: string; + writebackStatusVisibility?: string; surfaceAudience?: string; workVisibility?: string; nextStepKind?: string; diff --git a/packages/runtime/src/nextBestStep.test.ts b/packages/runtime/src/nextBestStep.test.ts index c0ef181..b23566b 100644 --- a/packages/runtime/src/nextBestStep.test.ts +++ b/packages/runtime/src/nextBestStep.test.ts @@ -654,6 +654,28 @@ describe("selectNextBestStep", () => { expect(result.primary.workVisibility).toBe("developer_visible"); }); + it("stops when developer-labeled work has user-visible impact", () => { + const result = selectNextBestStep( + baseState, + undefined, + null, + undefined, + undefined, + null, + { + surfaceAudience: "developer", + workVisibility: "user_visible" + } + ); + + expect(result.primary.actionId).toBe("open-current-phase"); + expect(result.primary.operatingMode).toBe("full-governance"); + expect(result.primary.outputShape).toBe("audit-skeleton"); + expect(result.primary.surfaceAudience).toBe("developer"); + expect(result.primary.workVisibility).toBe("user_visible"); + expect(result.primary.reason).toContain("user-visible behavior"); + }); + it("lets local operator surfaces continue when they do not change workflow semantics", () => { const result = selectNextBestStep( baseState, @@ -730,6 +752,106 @@ describe("selectNextBestStep", () => { expect(result.primary.actionId).toBe("open-current-phase"); expect(result.primary.nextStepKind).toBe("value-heartbeat"); expect(result.primary.operatingMode).toBe("normal-implementation"); - expect(result.primary.label).toBe("做一次价值 heartbeat"); + expect(result.primary.label).toBe("做一次价值 checkpoint"); + }); + + it("continues a bounded work bundle with daily progress metadata", () => { + const result = selectNextBestStep( + baseState, + undefined, + null, + undefined, + undefined, + null, + { + workBundleCandidate: true, + workBundleActionCount: 3, + surfaceAudience: "developer" + } + ); + + expect(result.primary.actionId).toBe("advance-phase"); + expect(result.primary.label).toBe("继续当前 work bundle"); + expect(result.primary.outputShape).toBe("progress-card"); + expect(result.primary.rolePacketPolicy).toBe("skip-daily"); + expect(result.primary.writebackStatusVisibility).toBe("optional"); + expect(result.primary.surfaceAudience).toBe("developer"); + }); + + it("turns an exhausted rolling bundle into a value checkpoint", () => { + const result = selectNextBestStep( + baseState, + undefined, + null, + undefined, + undefined, + null, + { + rollingCloseoutAuthorized: true, + workBundleCandidate: true, + workBundleActionCount: 4 + } + ); + + expect(result.primary.actionId).toBe("open-current-phase"); + expect(result.primary.nextStepKind).toBe("value-heartbeat"); + expect(result.primary.outputShape).toBe("progress-card"); + expect(result.primary.label).toBe("做一次价值 checkpoint"); + }); + + it("does not keep stretching a work bundle past the action budget", () => { + const result = selectNextBestStep( + baseState, + undefined, + null, + undefined, + undefined, + null, + { + workBundleCandidate: true, + workBundleActionCount: 5 + } + ); + + expect(result.primary.actionId).toBe("open-current-phase"); + expect(result.primary.nextStepKind).toBe("value-heartbeat"); + expect(result.primary.label).toBe("做一次价值 checkpoint"); + }); + + it("uses a value checkpoint after repeated internal-only work", () => { + const result = selectNextBestStep( + baseState, + undefined, + null, + undefined, + undefined, + null, + { + repeatedInternalOnlyCount: 3 + } + ); + + expect(result.primary.actionId).toBe("open-current-phase"); + expect(result.primary.nextStepKind).toBe("value-heartbeat"); + expect(result.primary.outputShape).toBe("progress-card"); + expect(result.primary.reason).toContain("内部工程"); + }); + + it("uses a value checkpoint after repeated gap checks", () => { + const result = selectNextBestStep( + baseState, + undefined, + null, + undefined, + undefined, + null, + { + repeatedGapChecks: true + } + ); + + expect(result.primary.actionId).toBe("open-current-phase"); + expect(result.primary.nextStepKind).toBe("value-heartbeat"); + expect(result.primary.reason).toContain("gap check"); }); }); diff --git a/packages/runtime/src/nextBestStep.ts b/packages/runtime/src/nextBestStep.ts index a0b2273..9ed46e5 100644 --- a/packages/runtime/src/nextBestStep.ts +++ b/packages/runtime/src/nextBestStep.ts @@ -19,6 +19,7 @@ export type { RuntimeVerificationLevel, SurfaceAudience, WorkVisibility, + RolePacketPolicy, NextBestStepDecision, RuntimeActionId } from "./nextBestStepModel.ts"; @@ -39,6 +40,11 @@ export interface AdaptiveWorkSessionSignals { changesDefaultBehavior?: boolean; compatibilityRisk?: boolean; publicMisuseRisk?: boolean; + workBundleCandidate?: boolean; + workBundleActionCount?: number; + rollingCloseoutAuthorized?: boolean; + repeatedGapChecks?: boolean; + repeatedInternalOnlyCount?: number; lightRepair?: boolean; lightRepairReason?: string; legacyMetadataMissing?: boolean; @@ -113,6 +119,7 @@ function isAuditStopRequired(signals: AdaptiveWorkSessionSignals) { signals.requiresAuditStop || signals.introducesConsumerSurface || signals.changesProductSemantics || + signals.workVisibility === "user_visible" || signals.surfaceAudience === "user_public" || (signals.surfaceAudience === "operator" && (signals.changesWorkflowSemantics || @@ -140,6 +147,10 @@ function auditStopReason(signals: AdaptiveWorkSessionSignals) { return "下一步会影响 user/public surface,必须先确认兼容性、验收和 stop condition。"; } + if (signals.workVisibility === "user_visible") { + return "下一步虽然入口不一定是 public surface,但结果已经会影响 user-visible behavior,必须先确认兼容性、验收和 stop condition。"; + } + if (signals.surfaceAudience === "operator") { return "下一步会改变 operator surface 的长期用法或默认行为,必须先确认 workflow 语义。"; } @@ -158,6 +169,19 @@ function hasThreeConsecutiveGovernanceHeavyCloseouts( ); } +function shouldRecommendValueCheckpoint(signals: AdaptiveWorkSessionSignals) { + return ( + hasThreeConsecutiveGovernanceHeavyCloseouts( + signals.closeoutTiersSinceValueHeartbeat + ) || + (signals.repeatedInternalOnlyCount ?? 0) >= 3 || + Boolean(signals.repeatedGapChecks) || + (signals.workBundleCandidate && (signals.workBundleActionCount ?? 0) > 4) || + (signals.rollingCloseoutAuthorized && + (signals.workBundleActionCount ?? 0) >= 4) + ); +} + function canContinueWorkSession(state: ProjectState, pendingUserDecision: unknown) { return ( !pendingUserDecision && @@ -171,13 +195,17 @@ function canContinueWorkSession(state: ProjectState, pendingUserDecision: unknow function normalImplementationMetadata( capabilityTranslation: string, surfaceAudience: SurfaceAudience = "internal", - workVisibility: WorkVisibility = defaultVisibilityForAudience(surfaceAudience) + workVisibility: WorkVisibility = defaultVisibilityForAudience(surfaceAudience), + rolePacketPolicy: RolePacketPolicy = "skip-daily" ) { return { operatingMode: "normal-implementation" as const, writebackTier: "current-context" as const, verificationLevel: "standard" as const, outputBudget: "standard" as const, + outputShape: "progress-card" as const, + rolePacketPolicy, + writebackStatusVisibility: "optional" as const, surfaceAudience, workVisibility, affectedLayer: "runtime recommendation", @@ -195,6 +223,9 @@ function fullGovernanceMetadata( writebackTier: "committed-truth" as const, verificationLevel: "release" as const, outputBudget: "audit" as const, + outputShape: "audit-skeleton" as const, + rolePacketPolicy: "refresh-durable" as const, + writebackStatusVisibility: "required" as const, surfaceAudience: audience, workVisibility: visibility, affectedLayer: "governance boundary", @@ -208,6 +239,9 @@ function lightRepairMetadata(capabilityTranslation: string) { writebackTier: "evidence-only" as const, verificationLevel: "narrow" as const, outputBudget: "lite" as const, + outputShape: "progress-card" as const, + rolePacketPolicy: "skip-daily" as const, + writebackStatusVisibility: "omit" as const, surfaceAudience: "internal" as const, workVisibility: "internal" as const, affectedLayer: "focused repair", @@ -235,6 +269,14 @@ function auditStopMetadata( ); } + if (signals.workVisibility === "user_visible") { + return fullGovernanceMetadata( + capabilityTranslation, + signals.surfaceAudience ?? "user_public", + "user_visible" + ); + } + if (signals.surfaceAudience === "operator") { return fullGovernanceMetadata( capabilityTranslation, @@ -815,18 +857,14 @@ export function selectNextBestStep( }; } - if ( - hasThreeConsecutiveGovernanceHeavyCloseouts( - adaptiveSignals.closeoutTiersSinceValueHeartbeat - ) - ) { + if (shouldRecommendValueCheckpoint(adaptiveSignals)) { return { primary: recommendation( "open-current-phase", - "做一次价值 heartbeat", - "最近已经连续完成三次 governance-heavy closeout。现在适合轻量确认:项目是否更可用、更可靠或更接近目标,以及下一步继续深挖工程是否仍然最高价值。", + "做一次价值 checkpoint", + "最近的推进已经连续偏向内部工程、治理或 gap check。现在适合轻量确认:继续工程深挖,还是切到用户可见价值、产品体验或验证质量。", ["planner"], - "操作者已接受、跳过或完成这次 value heartbeat,下一步方向重新对齐。", + "操作者已接受、跳过或完成这次 value checkpoint,下一步方向重新对齐。", { nextStepKind: "value-heartbeat", ...normalImplementationMetadata( @@ -862,28 +900,39 @@ export function selectNextBestStep( } const surfaceMetadata = resolveSurfaceMetadata(adaptiveSignals); + const canBundle = + adaptiveSignals.workBundleCandidate && + (adaptiveSignals.workBundleActionCount ?? 1) <= 4; + const nextLabel = adaptiveSignals.lightRepair + ? "执行轻量修复" + : canBundle + ? adaptiveSignals.rollingCloseoutAuthorized + ? "继续 rolling work bundle" + : "继续当前 work bundle" + : canContinueWorkSession(state, pendingUserDecision) + ? "继续当前 work session" + : "推进当前 phase"; + const nextReason = adaptiveSignals.lightRepair + ? adaptiveSignals.lightRepairReason?.trim() || + "这是一个不声明 durable phase acceptance 的窄修复;下一步应做 focused change 和 narrow verification。" + : adaptiveSignals.previousGapCheckSelectedImplementationPath + ? "上一轮 gap check 已经选出实现路径,且当前没有失败验证、范围变化或 audit stop;下一步应该进入实现,而不是再做一次 gap check。" + : canBundle + ? "下一组动作仍在同一 work bundle 内,适合用 Progress Card 连续推进,而不是拆成新的 phase approval。" + : "这是当前活跃项目里价值最高、且没有被阻塞的下一步。"; return { primary: recommendation( "advance-phase", - adaptiveSignals.lightRepair - ? "执行轻量修复" - : canContinueWorkSession(state, pendingUserDecision) - ? "继续当前 work session" - : "推进当前 phase", - adaptiveSignals.lightRepair - ? adaptiveSignals.lightRepairReason?.trim() || - "这是一个不声明 durable phase acceptance 的窄修复;下一步应做 focused change 和 narrow verification。" - : adaptiveSignals.previousGapCheckSelectedImplementationPath - ? "上一轮 gap check 已经选出实现路径,且当前没有失败验证、范围变化或 audit stop;下一步应该进入实现,而不是再做一次 gap check。" - : "这是当前活跃项目里价值最高、且没有被阻塞的下一步。", + nextLabel, + nextReason, ["planner", "executor", "reviewer"], "当前 slice 到达待评审或待验证状态。", adaptiveSignals.lightRepair ? lightRepairMetadata( "小修复只需要证明局部行为正确,不把它升级成完整阶段验收。" ) - : canContinueWorkSession(state, pendingUserDecision) + : canBundle || canContinueWorkSession(state, pendingUserDecision) ? { nextStepKind: "work-session-continue", ...normalImplementationMetadata( diff --git a/packages/runtime/src/nextBestStepModel.ts b/packages/runtime/src/nextBestStepModel.ts index 86c617a..274a7ae 100644 --- a/packages/runtime/src/nextBestStepModel.ts +++ b/packages/runtime/src/nextBestStepModel.ts @@ -26,6 +26,12 @@ export type RuntimeVerificationLevel = "narrow" | "standard" | "release"; export type OutputBudget = "lite" | "standard" | "audit"; +export type OutputShape = "progress-card" | "audit-skeleton" | "compact"; + +export type RolePacketPolicy = "skip-daily" | "refresh-durable"; + +export type WritebackStatusVisibility = "omit" | "optional" | "required"; + export type SurfaceAudience = | "internal" | "developer" @@ -44,6 +50,9 @@ export interface RecommendationMetadata { writebackTier?: WritebackTier; verificationLevel?: RuntimeVerificationLevel; outputBudget?: OutputBudget; + outputShape?: OutputShape; + rolePacketPolicy?: RolePacketPolicy; + writebackStatusVisibility?: WritebackStatusVisibility; surfaceAudience?: SurfaceAudience; workVisibility?: WorkVisibility; affectedLayer?: string; @@ -57,6 +66,9 @@ export interface ActionRecommendation { writebackTier?: WritebackTier; verificationLevel?: RuntimeVerificationLevel; outputBudget?: OutputBudget; + outputShape?: OutputShape; + rolePacketPolicy?: RolePacketPolicy; + writebackStatusVisibility?: WritebackStatusVisibility; surfaceAudience?: SurfaceAudience; workVisibility?: WorkVisibility; affectedLayer?: string; diff --git a/scripts/verify-threadsmith-skill-contract.mjs b/scripts/verify-threadsmith-skill-contract.mjs index 157549c..7ed98df 100644 --- a/scripts/verify-threadsmith-skill-contract.mjs +++ b/scripts/verify-threadsmith-skill-contract.mjs @@ -99,6 +99,7 @@ const checks = [ /3-5 concise lines/, /short closeout with capability/, /full audit skeleton only when a real audit boundary exists/, + /Daily Progress Card/, /Avoid foregrounding dense protocol terms/, /capability translation/ ] @@ -108,11 +109,15 @@ const checks = [ label: "output matrix and orientation sections are required", patterns: [ /## Output Matrix/, + /`daily-progress`/, /`lite`/, /`standard`/, /`audit`/, + /### Daily Progress Card/, + /Do not show `Threadsmith Decision` in daily progress output/, + /Output selection precedence/, /## Closeout Output Gate/, - /If any two of these signals are present/, + /Daily\s+Progress is not eligible/s, /commit, PR, merge, tag, release, durable truth writeback, packet update, or\s+closeout artifact/, /Threadsmith output rules override the default concise Codex final style/, /exact field skeleton/, @@ -294,7 +299,8 @@ const checks = [ patterns: [ /## Adaptive Work Session Rule/, /2-4 actions/, - /new UI route \/ API endpoint \/ CLI command/, + /new user-public UI route \/ API endpoint \/ CLI command/, + /Developer-only local CLI/, /## Closeout Tier Rule/, /## Gap Check Budget Rule/, /## Product \/ User-Value Heartbeat Rule/, @@ -515,6 +521,9 @@ const checks = [ /writeback tier: `evidence-only`, `current-context`, or `committed-truth`/, /surface audience: `internal`, `developer`, `operator`, or `user_public`/, /work visibility: `internal`, `developer_visible`, `operator_visible`, or\s+`user_visible`/, + /output shape/, + /role packet policy/, + /writeback status visibility/, /heartbeat-compatible closeout evidence/, /valueHeartbeatShown/, /phase history as the long-term counter source/,