dsh-tower 2.1.0-beta.1 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (75) hide show
  1. package/CHANGELOG.md +51 -0
  2. package/COMPATIBILITY.md +1 -0
  3. package/CONTRIBUTING.md +8 -1
  4. package/README.en.md +8 -2
  5. package/README.md +8 -2
  6. package/adapters/default.js +2 -0
  7. package/app/archive.js +190 -0
  8. package/app/board.js +3 -1
  9. package/app/checkCompletion.js +15 -2
  10. package/app/gate.js +31 -13
  11. package/app/init.js +45 -3
  12. package/app/merge.js +6 -0
  13. package/app/mission.js +2 -0
  14. package/app/plan.js +12 -4
  15. package/app/read.js +59 -3
  16. package/app/review.js +2 -0
  17. package/app/teardown.js +22 -1
  18. package/app/tower.js +34 -6
  19. package/docs/agents/domain.md +38 -0
  20. package/docs/agents/issue-tracker.md +46 -0
  21. package/docs/agents/triage-labels.md +15 -0
  22. package/docs/architecture/overview.md +3 -1
  23. package/docs/guide/install-upgrade-migration.md +18 -1
  24. package/docs/reference/blockers.md +1 -1
  25. package/docs/reference/config.generated.md +4 -0
  26. package/docs/reference/config.md +4 -0
  27. package/docs/reference/doctor.md +58 -8
  28. package/docs/reference/errors.generated.md +2 -0
  29. package/docs/reference/errors.md +4 -2
  30. package/docs/reference/tools.generated.md +6 -6
  31. package/docs/reference/tools.md +7 -6
  32. package/docs/zh/guide/install-upgrade-migration.md +13 -1
  33. package/docs/zh/reference/blockers.md +1 -1
  34. package/docs/zh/reference/config.md +4 -0
  35. package/docs/zh/reference/doctor.md +35 -2
  36. package/docs/zh/reference/errors.md +4 -2
  37. package/docs/zh/reference/tools.md +3 -3
  38. package/domain/config/schema.js +26 -3
  39. package/domain/contracts/outputs.js +27 -1
  40. package/domain/contracts/tool-inputs/missions.js +2 -1
  41. package/domain/errors.js +2 -0
  42. package/domain/integration/gate.js +25 -11
  43. package/domain/integration/init.js +40 -4
  44. package/domain/mission.js +1 -1
  45. package/domain/missions/completion.js +58 -5
  46. package/domain/missions/lifecycle.js +2 -1
  47. package/domain/planning.js +6 -3
  48. package/domain/read/views.js +14 -1
  49. package/domain/report.js +9 -1
  50. package/domain/scope.js +83 -11
  51. package/domain/state/archive.js +257 -0
  52. package/domain/state/migrations.js +145 -0
  53. package/domain/state/schema.js +6 -0
  54. package/domain/state.js +154 -4
  55. package/domain/summary.js +2 -0
  56. package/lib/archive.js +279 -0
  57. package/lib/caseSensitivity.js +66 -0
  58. package/lib/doctor.js +178 -0
  59. package/lib/leases.js +14 -5
  60. package/lib/legacy.js +19 -5
  61. package/lib/persistence.js +12 -5
  62. package/lib/stateFile.js +99 -0
  63. package/lib/store.js +15 -0
  64. package/lib/tests.js +6 -2
  65. package/lib/worktreeOwnership.js +18 -7
  66. package/package.json +1 -1
  67. package/scripts/pack-manifest.txt +9 -0
  68. package/tools/complete.js +1 -1
  69. package/tools/doctor.js +1 -1
  70. package/tools/init.js +1 -1
  71. package/tools/plan.js +1 -1
  72. package/tools/preflight.js +1 -0
  73. package/tools/status.js +14 -6
  74. package/tools/teardown.js +1 -1
  75. package/types/index.d.ts +34 -0
package/tools/status.js CHANGED
@@ -4,6 +4,7 @@ import { SCHEMAS, output } from '../domain/contracts/outputs.js'
4
4
  import { classifyError } from '../domain/errors.js'
5
5
  import { StatusInput } from '../domain/contracts/tool-inputs/index.js'
6
6
  import { ROSTER_INACTIVE_STATES, isMarkdownFormat, isReservedState, isWorkerRole } from '../domain/vocabulary.js'
7
+ import { archiveView } from '../app/archive.js'
7
8
 
8
9
  /**
9
10
  * @param {any} ctx
@@ -19,9 +20,9 @@ export function registerStatusTool(ctx, deps) {
19
20
  define(ctx, {
20
21
  name: 'tower_status',
21
22
  description:
22
- 'Read the Tower dashboard for a repository: state path, base branch, revision, mission status counts, paged mission summaries (scope/deps/tasks/worktree/owner), roster size, findings, config path and the activity tail. Pass missionId for one full mission (tasks, notes, reviews, findings, completion report); offset/limit page the mission list (default 20, max 100); includeGate adds preflight gate digests for the page; format:"markdown" returns the human-readable mission board. Read-only.',
23
+ 'Read the Tower dashboard for a repository: state path, base branch, revision, mission status counts, paged mission summaries (scope/deps/tasks/worktree/owner), roster size, findings, archive count, config path and the activity tail. Pass missionId for one full mission (tasks, notes, reviews, findings, completion report; archived missions are loaded from their year file); offset/limit page the mission list (default 20, max 100); includeGate adds preflight gate digests for the page; includeArchived appends compact archived summaries after the active page; format:"markdown" returns the human-readable mission board (active missions plus an archived count). Read-only.',
23
24
  parameters: StatusInput,
24
- aliases: { mission_id: ['missionId'], include_gate: ['includeGate'] },
25
+ aliases: { mission_id: ['missionId'], include_gate: ['includeGate'], include_archived: ['includeArchived'] },
25
26
  output: output(SCHEMAS.status),
26
27
  async execute(args, exec) {
27
28
  const tower = await Tower.open(args.repoRoot)
@@ -48,11 +49,18 @@ export function registerStatusTool(ctx, deps) {
48
49
  }
49
50
  }
50
51
  const state = await tower.load()
51
- const total = state.missions.length
52
+ const index = await tower.archiveIndex()
53
+ const activeIds = state.missions.map((mission) => mission.id)
54
+ const archivedIds = index.ok
55
+ ? index.missions.map((entry) => entry.id).filter((id) => !activeIds.includes(id))
56
+ : []
57
+ // Hot state pages first, then archived summaries on demand (#73).
58
+ const ids = args.includeArchived === true ? [...activeIds, ...archivedIds] : activeIds
59
+ const total = ids.length
52
60
  const offset = typeof args.offset === 'number' && Number.isInteger(args.offset) && args.offset > 0 ? args.offset : 0
53
61
  const limit = typeof args.limit === 'number' && Number.isInteger(args.limit) ? Math.max(1, Math.min(100, args.limit)) : 20
54
- const pageIds = state.missions.slice(offset, offset + limit).map((mission) => mission.id)
55
- const status = await tower.status({ missionIds: pageIds })
62
+ const pageIds = ids.slice(offset, offset + limit)
63
+ const status = await tower.status({ missionIds: pageIds, includeArchived: args.includeArchived === true })
56
64
  let page = status.missions
57
65
  if (args.includeGate === true) {
58
66
  const { activity, parentStatus } = await observeActivity(ctx, tower)
@@ -86,7 +94,7 @@ export function registerStatusTool(ctx, deps) {
86
94
  if (isMarkdownFormat(args.format)) {
87
95
  const state = await tower.load()
88
96
  const output = /** @type {any} */ (result)
89
- output.markdown = tower.boardMarkdown(state)
97
+ output.markdown = tower.boardMarkdown(state, archiveView(index))
90
98
  }
91
99
  return result
92
100
  },
package/tools/teardown.js CHANGED
@@ -21,7 +21,7 @@ export function registerTeardownTool(ctx, deps) {
21
21
  define(ctx, {
22
22
  name: 'tower_teardown',
23
23
  description:
24
- 'Tear down eligible mission worktrees after their missions are merged or abandoned. A worktree whose roster agent is running is always kept (force cannot override); dirty worktrees need force; exclude keeps named missions. dry_run previews the remove/keep decisions without changing anything.',
24
+ 'Tear down eligible mission worktrees after their missions are merged or abandoned. A worktree whose roster agent is running is always kept (force cannot override); dirty worktrees need force; exclude keeps named missions. dry_run previews the remove/keep decisions without changing anything. Terminal missions whose worktree is gone and whose configured archive policy is due (archive.afterDays / archive.maxMissions) are moved to <git-common-dir>/dsh-tower/archive/ so state.json stops rewriting them; nothing is deleted.',
25
25
  parameters: TeardownInput,
26
26
  aliases: {"dry_run":["dryRun"]},
27
27
  board: true,
package/types/index.d.ts CHANGED
@@ -99,6 +99,8 @@ export interface CompletionCheck {
99
99
 
100
100
  export interface CompletionReport {
101
101
  report?: string
102
+ /** UTF-8 byte length of `report`; absent on documents written before #89. */
103
+ reportBytes?: number
102
104
  by?: string
103
105
  at?: number
104
106
  checks?: CompletionCheck[]
@@ -153,6 +155,34 @@ export interface Mission {
153
155
  authorizationRef?: string | null
154
156
  }
155
157
 
158
+ /**
159
+ * Compact archived-mission entry (`archive/index.json`). It keeps enough of the
160
+ * mission for ordering, dependency lookups and id allocation without loading the
161
+ * full year file, and it is what `tower_status includeArchived` pages from (#73).
162
+ */
163
+ export interface ArchiveIndexEntry {
164
+ id: string
165
+ title: string
166
+ status: MissionStatus
167
+ kind?: MissionKind
168
+ scope?: string[]
169
+ deps?: string[]
170
+ owner?: string | null
171
+ branch?: string | null
172
+ mergedCommit?: string | null
173
+ mergedAt?: number | null
174
+ updatedAt?: number | null
175
+ archivedAt: number
176
+ archivedYear: number
177
+ }
178
+
179
+ /** Full archived record (`archive/<year>.json`): mission + linked history. */
180
+ export interface ArchivedMissionRecord extends ArchiveIndexEntry {
181
+ mission: Mission
182
+ reviews: Review[]
183
+ findings: Finding[]
184
+ }
185
+
156
186
  export interface Review {
157
187
  id?: string
158
188
  seq?: number
@@ -220,6 +250,8 @@ export interface TowerState {
220
250
  createdAt?: number
221
251
  updatedAt?: number
222
252
  dirtyBase?: boolean
253
+ /** Detected once at tower_init; null means "not detected yet" (#90). */
254
+ caseInsensitive?: boolean | null
223
255
  towerAgentId?: string | null
224
256
  snapshotCommit?: string | null
225
257
  snapshotRef?: string | null
@@ -268,6 +300,8 @@ export interface MissionSummary {
268
300
  reviewPolicy: ReviewPolicy
269
301
  mergedCommit: string | null
270
302
  mergedAt: number | null
303
+ /** Size of the completion report without loading its body; null when absent. */
304
+ reportBytes: number | null
271
305
  updatedAt?: number
272
306
  }
273
307