dsh-tower 2.4.2 → 2.6.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.
- package/CHANGELOG.md +72 -0
- package/COMPATIBILITY.md +2 -0
- package/CONTRIBUTING.md +5 -3
- package/GLOSSARY.md +73 -0
- package/PRINCIPLES.md +15 -7
- package/README.en.md +16 -15
- package/README.md +18 -10
- package/app/archive.js +16 -1
- package/app/board.js +16 -1
- package/app/gate.js +14 -0
- package/app/review.js +7 -0
- package/app/spawn-prechecks.js +30 -5
- package/app/tower.js +5 -5
- package/client.js +321 -0
- package/cordis.patch.yml +26 -9
- package/docs/adr/0004-surface-budget.md +10 -4
- package/docs/adr/0005-component-surface.md +222 -11
- package/docs/architecture/overview.md +12 -6
- package/docs/examples/first-mission.md +3 -2
- package/docs/faq.md +9 -7
- package/docs/guide/install-upgrade-migration.md +53 -7
- package/docs/guide/quickstart.md +91 -10
- package/docs/guide/release.md +60 -19
- package/docs/guide/scenarios-and-philosophy.md +35 -28
- package/docs/operations/security.md +11 -10
- package/docs/reference/blockers.md +12 -7
- package/docs/reference/component-surface.md +40 -21
- package/docs/reference/config.generated.md +6 -8
- package/docs/reference/config.md +36 -8
- package/docs/reference/doctor.md +11 -5
- package/docs/reference/errors.generated.md +1 -0
- package/docs/reference/errors.md +2 -1
- package/docs/reference/surface-budget.md +47 -12
- package/docs/reference/testing.md +1 -1
- package/docs/reference/tools.generated.md +5 -5
- package/docs/reference/tools.md +5 -5
- package/docs/zh/faq.md +7 -5
- package/docs/zh/guide/install-upgrade-migration.md +26 -7
- package/docs/zh/guide/quickstart.md +17 -10
- package/docs/zh/guide/scenarios-and-philosophy.md +23 -15
- package/docs/zh/operations/security.md +9 -8
- package/docs/zh/reference/blockers.md +11 -7
- package/docs/zh/reference/config.md +28 -8
- package/docs/zh/reference/doctor.md +7 -2
- package/docs/zh/reference/errors.md +2 -1
- package/docs/zh/reference/testing.md +2 -2
- package/docs/zh/reference/tools.md +6 -5
- package/domain/component-rules.js +216 -0
- package/domain/components.js +181 -53
- package/domain/config/schema.js +86 -28
- package/domain/contracts/outputs.js +2 -0
- package/domain/contracts/tool-inputs/missions.js +1 -1
- package/domain/errors.js +6 -0
- package/domain/gate.js +23 -5
- package/domain/integration/review.js +203 -9
- package/domain/scope.js +176 -5
- package/domain/surface.js +21 -7
- package/host/capability-archive.js +18 -0
- package/host/capability-board.js +18 -0
- package/host/capability-registry.js +4 -0
- package/host/config.js +40 -3
- package/host/doctor.js +68 -12
- package/host/presets.js +26 -8
- package/host/tool-registry.js +30 -5
- package/index.js +17 -6
- package/locale/archive/en.json +6 -0
- package/locale/archive/zh.json +6 -0
- package/locale/batch/en.json +1 -1
- package/locale/batch/zh.json +1 -1
- package/locale/board/en.json +6 -0
- package/locale/board/zh.json +6 -0
- package/locale/en.json +37 -22
- package/locale/hmr/en.json +2 -2
- package/locale/hmr/zh.json +1 -1
- package/locale/import/en.json +1 -1
- package/locale/import/zh.json +1 -1
- package/locale/leases/en.json +2 -2
- package/locale/leases/zh.json +2 -2
- package/locale/preset-reviewer/en.json +1 -1
- package/locale/preset-reviewer/zh.json +1 -1
- package/locale/preset-worker/en.json +1 -1
- package/locale/preset-worker/zh.json +1 -1
- package/locale/skills/en.json +1 -1
- package/locale/skills/zh.json +1 -1
- package/locale/tests/en.json +2 -2
- package/locale/tests/zh.json +2 -2
- package/locale/zh.json +36 -21
- package/package.json +17 -2
- package/scripts/check-components.mjs +698 -44
- package/scripts/check-pack.mjs +3 -0
- package/scripts/component-surface.json +101 -37
- package/scripts/generate-capability-locale.mjs +3 -0
- package/scripts/mount-smoke/surface.mjs +2 -0
- package/scripts/pack-manifest.txt +10 -0
- package/scripts/release-notes.mjs +256 -0
- package/scripts/surface-baseline.json +39 -3
- package/scripts/surface-baseline.mjs +15 -5
- package/scripts/types-coverage.mjs +7 -0
- package/skills.js +4 -1
- package/tools/complete.js +3 -1
- package/tools/preflight.js +1 -1
- package/tools/review.js +1 -1
- package/tools/spawn.js +7 -7
- package/tools/status.js +8 -1
- package/tools/test.js +7 -7
- package/types/index.d.ts +10 -0
- package/ui-bundle/locale/en.json +1 -1
- package/ui-bundle/locale/zh.json +1 -1
|
@@ -8,8 +8,9 @@
|
|
|
8
8
|
// tools every tool the Host entry registers, including the
|
|
9
9
|
// tower_mode/tower_control pair that index.js registers
|
|
10
10
|
// directly rather than through define() (#118)
|
|
11
|
-
// components user-visible rows in cordis.patch.yml (#120
|
|
12
|
-
// 4 -> 1 contraction and any future move
|
|
11
|
+
// components user-visible rows in every shipped cordis.patch.yml (#120,
|
|
12
|
+
// #134), so the 2.3.0 4 -> 1 contraction and any future move
|
|
13
|
+
// need a written decision
|
|
13
14
|
//
|
|
14
15
|
// `scripts/surface-baseline.json` freezes those counts. A PR that grows a count
|
|
15
16
|
// fails here until the baseline carries a decision with either an ADR under
|
|
@@ -24,7 +25,7 @@ import fs from 'node:fs'
|
|
|
24
25
|
import path from 'node:path'
|
|
25
26
|
import { fileURLToPath } from 'node:url'
|
|
26
27
|
import { configSurface } from '../domain/config/schema.js'
|
|
27
|
-
import { parseCordisPatch } from '../domain/components.js'
|
|
28
|
+
import { COMPONENT_PATCH_FILES, mergeCordisPatches, parseCordisPatch } from '../domain/components.js'
|
|
28
29
|
import { SURFACE_CLASSIFICATION_FILE, SURFACE_BASELINE_FILE, surfaceLine } from '../domain/surface.js'
|
|
29
30
|
import { loadShippedToolSurface } from './lib/tool-surface.mjs'
|
|
30
31
|
import { checkSurfaceBudget } from './lib/surface.mjs'
|
|
@@ -42,7 +43,8 @@ with a decision that names an existing docs/adr/ ADR, deletions that are no
|
|
|
42
43
|
longer in the live surface, or a measurement-correction record with a reason and
|
|
43
44
|
date. The classification table in
|
|
44
45
|
${SURFACE_CLASSIFICATION_FILE} must cover every key and tool exactly once, and
|
|
45
|
-
the component count comes from the visible rows in
|
|
46
|
+
the component count comes from the visible rows in every shipped
|
|
47
|
+
cordis.patch.yml.`
|
|
46
48
|
|
|
47
49
|
/**
|
|
48
50
|
* The live tool names: apply the shipped Host entry so every registration path
|
|
@@ -65,7 +67,15 @@ function liveToolNames() {
|
|
|
65
67
|
export function liveSurface(root = PACKAGE_ROOT) {
|
|
66
68
|
const config = configSurface()
|
|
67
69
|
const tools = liveToolNames()
|
|
68
|
-
|
|
70
|
+
// #134: the counted component surface is every shipped patch, not only the
|
|
71
|
+
// root one; `ui-bundle/cordis.patch.yml` ships the tower-ui row.
|
|
72
|
+
const patches = {}
|
|
73
|
+
for (const file of COMPONENT_PATCH_FILES) {
|
|
74
|
+
const patchFile = path.join(root, file)
|
|
75
|
+
if (!fs.existsSync(patchFile)) continue
|
|
76
|
+
patches[file] = parseCordisPatch(fs.readFileSync(patchFile, 'utf8'))
|
|
77
|
+
}
|
|
78
|
+
const patch = mergeCordisPatches(patches)
|
|
69
79
|
return {
|
|
70
80
|
configKeys: config.count,
|
|
71
81
|
sections: config.sections.length,
|
|
@@ -14,6 +14,9 @@
|
|
|
14
14
|
// changing the tsconfig is enough and this gate follows.
|
|
15
15
|
//
|
|
16
16
|
// Excluded tiers are data with a reason, not silent gaps:
|
|
17
|
+
// - `client.js` is the core Web Client half (#M40): a browser artifact checked
|
|
18
|
+
// by tests/plugin-config-client.test.mjs and the client rules, not by the
|
|
19
|
+
// Node tsc program;
|
|
17
20
|
// - `ui-bundle/**` is the optional Web client: a browser artifact with DOM
|
|
18
21
|
// types and no Node runtime, checked by tests/ui-bundle.test.mjs and the
|
|
19
22
|
// architecture client rules instead of the Node tsc program;
|
|
@@ -30,6 +33,10 @@ const WALK_SKIP = new Set(['node_modules', '.git', '.dsh-tower-worktrees', 'cove
|
|
|
30
33
|
|
|
31
34
|
/** Shipped `.js` tiers that are deliberately outside the Node tsc program. */
|
|
32
35
|
export const NOT_CHECKED_TIERS = [
|
|
36
|
+
{
|
|
37
|
+
pattern: 'client.js',
|
|
38
|
+
reason: 'core Web client: browser artifact, checked by tests/plugin-config-client.test.mjs',
|
|
39
|
+
},
|
|
33
40
|
{
|
|
34
41
|
pattern: 'ui-bundle/**',
|
|
35
42
|
reason: 'optional Web client: browser artifact, checked by tests/ui-bundle.test.mjs and the client architecture rules',
|
package/skills.js
CHANGED
|
@@ -66,5 +66,8 @@ export function apply(ctx) {
|
|
|
66
66
|
// lets tower_status/tower_doctor report "disabled by component tower-skills"
|
|
67
67
|
// when the row is off instead of leaving the missing catalog unexplained.
|
|
68
68
|
holdComponent(ctx, 'tower-skills')
|
|
69
|
-
|
|
69
|
+
// #139: the provider must be unregistered with the plugin, so the catalog stops
|
|
70
|
+
// serving after unload/reload instead of keeping a dead provider alive. Host
|
|
71
|
+
// registries may or may not return a disposer; `ctx.effect` accepts both.
|
|
72
|
+
ctx.effect(() => ctx.skills.registerProvider(() => provider))
|
|
70
73
|
}
|
package/tools/complete.js
CHANGED
|
@@ -31,7 +31,9 @@ export function registerCompleteTool(ctx, deps) {
|
|
|
31
31
|
const context = await tower.missionContext(args.missionId)
|
|
32
32
|
const checks = [...(Array.isArray(args.checks) ? args.checks : [])]
|
|
33
33
|
const testsConfig = /** @type {any} */ (tower.config.tests ?? {})
|
|
34
|
-
|
|
34
|
+
// #136: the `tower-tests` component row is the only switch; admission
|
|
35
|
+
// decides when discovery runs, not whether it may run.
|
|
36
|
+
if (isCompleteAdmission(testsConfig.admission)) {
|
|
35
37
|
const discovery = await runMissionDiscovery(tower, { id: args.missionId, worktree: context.mission.worktree })
|
|
36
38
|
if (discovery.configured && discovery.receipt.ok !== true) {
|
|
37
39
|
throw new TowerToolError('tests-failed', `test discovery failed for ${args.missionId}: ${discovery.receipt.error}`)
|
package/tools/preflight.js
CHANGED
|
@@ -12,7 +12,7 @@ export function registerPreflightTool(ctx, deps) {
|
|
|
12
12
|
define(ctx, {
|
|
13
13
|
name: 'tower_preflight',
|
|
14
14
|
description:
|
|
15
|
-
'Read-only merge gate for one mission: returns ok plus stable blocker codes (branch-missing, deps-unmerged, read-only-survey, stale-base, out-of-scope, base-dirty, branch-owned-by-closed-missions, and mission/worker guards). Review blockers (no-review, review-hold, not-clean, review-not-approved, tip-moved, review-mission-mismatch) apply only when review.
|
|
15
|
+
'Read-only merge gate for one mission: returns ok plus stable blocker codes (branch-missing, deps-unmerged, read-only-survey, stale-base, out-of-scope, base-dirty, branch-owned-by-closed-missions, and mission/worker guards). Review blockers (no-review, review-hold, not-clean, review-not-approved, tip-moved, review-mission-mismatch) apply only when review.mode is required; review.mode off and optional skip them. Never changes anything.',
|
|
16
16
|
parameters: PreflightInput,
|
|
17
17
|
aliases: {"missionId":["id"]},
|
|
18
18
|
requiresMissionId: true,
|
package/tools/review.js
CHANGED
|
@@ -13,7 +13,7 @@ export function registerReviewTool(ctx, deps) {
|
|
|
13
13
|
define(ctx, {
|
|
14
14
|
name: 'tower_review',
|
|
15
15
|
description:
|
|
16
|
-
'Submit an independent review verdict bound to the current branch tip: a completed mission starts a review round, and while the mission is active a later route may still record a verdict for that same reviewed tip, appending to the same round without changing the status (a moved tip is rejected with an explanation). status is "clean", "p1-Nitems", or "p2-Nitems"; merge is "merge", "fix-then-merge", or "hold" (only clean + merge is later approval). Non-clean returns the mission to active. Reviewers are capped at 5 rounds per branch; a tower-submitted review is break-glass and requires breakGlassReason.',
|
|
16
|
+
'Submit an independent review verdict bound to the current branch tip: a completed mission starts a review round, and while the mission is active a later route may still record a verdict for that same reviewed tip, appending to the same round without changing the status (a moved tip is rejected with an explanation). status is "clean", "p1-Nitems", or "p2-Nitems"; merge is "merge", "fix-then-merge", or "hold" (only clean + merge is later approval). Non-clean returns the mission to active. Reviewers are capped at 5 rounds per branch; a tower-submitted review is break-glass and requires breakGlassReason. The tool is not registered when review.mode is off.',
|
|
17
17
|
parameters: ReviewInput,
|
|
18
18
|
aliases: {"missionId":["id"]},
|
|
19
19
|
requiresMissionId: true,
|
package/tools/spawn.js
CHANGED
|
@@ -24,8 +24,8 @@ import { buildAgentOptions, buildSpawnBriefing, waitForSpawnResult } from '../ap
|
|
|
24
24
|
* recordSpawnThrottle: any,
|
|
25
25
|
* recordSpawnSuccess: any,
|
|
26
26
|
* runMissionDiscovery: any,
|
|
27
|
-
* textOf: any,
|
|
28
|
-
*
|
|
27
|
+
* textOf: any, inspectLeases: any,
|
|
28
|
+
* leaseComponent?: () => { enabled: boolean, reason: string },
|
|
29
29
|
* }} deps
|
|
30
30
|
*/
|
|
31
31
|
export function registerSpawnTool(ctx, deps) {
|
|
@@ -37,7 +37,7 @@ export function registerSpawnTool(ctx, deps) {
|
|
|
37
37
|
define(ctx, {
|
|
38
38
|
name: 'tower_spawn',
|
|
39
39
|
description:
|
|
40
|
-
'Spawn a worker or reviewer for a mission. Workers use the tower-worker preset; reviewers require a completed mission and use the read-only tower-reviewer preset. Provisions (or reuses) the mission git worktree and branch, generates a self-contained briefing, and starts a background subagent in that worktree. The child final report is delivered back to this session; tower_roster shows authoritative activity. Records the agent in the roster; reviewer spawns record the branch tip so a reviewer that modifies the branch cannot submit a review. Returns immediately unless wait=true.',
|
|
40
|
+
'Spawn a worker or reviewer for a mission. Workers use the tower-worker preset; reviewers require a completed mission and use the read-only tower-reviewer preset, and the reviewer role is refused when review.mode is off. Provisions (or reuses) the mission git worktree and branch, generates a self-contained briefing, and starts a background subagent in that worktree. The child final report is delivered back to this session; tower_roster shows authoritative activity. Records the agent in the roster; reviewer spawns record the branch tip so a reviewer that modifies the branch cannot submit a review. Returns immediately unless wait=true.',
|
|
41
41
|
parameters: SpawnInput,
|
|
42
42
|
aliases: {"missionId":["id"],"waitMs":["wait_ms"]},
|
|
43
43
|
requiresMissionId: true,
|
|
@@ -54,7 +54,7 @@ export function registerSpawnTool(ctx, deps) {
|
|
|
54
54
|
const preset = isReviewerRole(role) ? config.spawn.reviewerPreset : config.spawn.workerPreset
|
|
55
55
|
const model = isReviewerRole(role) ? config.spawn.reviewerModel : config.spawn.workerModel
|
|
56
56
|
const context = await tower.missionContext(args.missionId)
|
|
57
|
-
assertSpawnableMission({ role, context })
|
|
57
|
+
assertSpawnableMission({ role, context, config })
|
|
58
58
|
const spawned = await enqueueSpawn(tower.root, async () => {
|
|
59
59
|
const observed = await observeActivity(ctx, tower)
|
|
60
60
|
await tower.reconcileObservedInactive(observed.activity)
|
|
@@ -71,6 +71,8 @@ export function registerSpawnTool(ctx, deps) {
|
|
|
71
71
|
})
|
|
72
72
|
const { subagents, provider } = await precheckSpawnServices({
|
|
73
73
|
tower, ctx, config, preset, actor, exec, inspectLeases: deps.inspectLeases,
|
|
74
|
+
// #131: the app layer only needs the row fact; the root owns the registry.
|
|
75
|
+
leaseComponent: deps.leaseComponent,
|
|
74
76
|
})
|
|
75
77
|
|
|
76
78
|
const provisioned = await tower.provision({
|
|
@@ -134,9 +136,7 @@ export function registerSpawnTool(ctx, deps) {
|
|
|
134
136
|
if (classifyError(error).retryable === true) {
|
|
135
137
|
recordSpawnThrottle(tower.root, config.spawn.maxLiveWorkers)
|
|
136
138
|
}
|
|
137
|
-
// The child may already be running
|
|
138
|
-
// it and mark the reserved entry failed so it cannot keep writing
|
|
139
|
-
// unobserved.
|
|
139
|
+
// The child may already be running: stop it and fail the reserved entry.
|
|
140
140
|
if (agentId) {
|
|
141
141
|
try {
|
|
142
142
|
await Promise.resolve(subagents.interrupt(agentId, { kind: 'ancestor', agent: exec.agent }))
|
package/tools/status.js
CHANGED
|
@@ -4,6 +4,7 @@ import { classifyError } from '../domain/errors.js'
|
|
|
4
4
|
import { StatusInput } from '../domain/contracts/tool-inputs/index.js'
|
|
5
5
|
import { ROSTER_INACTIVE_STATES, isMarkdownFormat, isReservedState, isWorkerRole } from '../domain/vocabulary.js'
|
|
6
6
|
import { archiveView } from '../app/archive.js'
|
|
7
|
+
import { reviewPolicyView } from '../domain/integration/review.js'
|
|
7
8
|
|
|
8
9
|
/**
|
|
9
10
|
* @param {any} ctx
|
|
@@ -21,7 +22,7 @@ export function registerStatusTool(ctx, deps) {
|
|
|
21
22
|
define(ctx, {
|
|
22
23
|
name: 'tower_status',
|
|
23
24
|
description:
|
|
24
|
-
'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. Also reports the repo-scoped git lock holder (`gitLock: {held,pid,phase,heldMs}`) so a merge holding the mutex is visible. 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.',
|
|
25
|
+
'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. Also reports the repo-scoped git lock holder (`gitLock: {held,pid,phase,heldMs}`) so a merge holding the mutex is visible, and the resolved review policy (`review: {mode, available, required, detail}`; mode off means review is intentionally unavailable). 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.',
|
|
25
26
|
parameters: StatusInput,
|
|
26
27
|
aliases: { mission_id: ['missionId'], include_gate: ['includeGate'], include_archived: ['includeArchived'] },
|
|
27
28
|
output: output(SCHEMAS.status),
|
|
@@ -38,6 +39,10 @@ export function registerStatusTool(ctx, deps) {
|
|
|
38
39
|
(entry) => isWorkerRole(entry.role) && !ROSTER_INACTIVE_STATES.includes(entry.state) && !isReservedState(entry.state),
|
|
39
40
|
).length
|
|
40
41
|
const spawn = spawnBackoffView(tower.root, tower.config.spawn?.maxLiveWorkers, inflight)
|
|
42
|
+
// #M39: the resolved review policy (`{mode, available, required, detail}`)
|
|
43
|
+
// is a first-class dashboard fact. In `off` the detail says review is
|
|
44
|
+
// intentionally unavailable rather than leaving it as a silent absence.
|
|
45
|
+
const review = reviewPolicyView(tower.config)
|
|
41
46
|
if (args.missionId) {
|
|
42
47
|
const [status, detail] = await Promise.all([
|
|
43
48
|
tower.status({ missionIds: [args.missionId] }),
|
|
@@ -51,6 +56,7 @@ export function registerStatusTool(ctx, deps) {
|
|
|
51
56
|
boardPath,
|
|
52
57
|
mode,
|
|
53
58
|
spawn,
|
|
59
|
+
review,
|
|
54
60
|
components,
|
|
55
61
|
}
|
|
56
62
|
}
|
|
@@ -96,6 +102,7 @@ export function registerStatusTool(ctx, deps) {
|
|
|
96
102
|
boardPath,
|
|
97
103
|
mode,
|
|
98
104
|
spawn,
|
|
105
|
+
review,
|
|
99
106
|
components,
|
|
100
107
|
}
|
|
101
108
|
if (isMarkdownFormat(args.format)) {
|
package/tools/test.js
CHANGED
|
@@ -37,13 +37,13 @@ export function registerTestTool(ctx, deps) {
|
|
|
37
37
|
await tower.assertWorktree(context.mission.worktree)
|
|
38
38
|
const discovery = await runMissionDiscovery(tower, context.mission)
|
|
39
39
|
if (!discovery.configured) {
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
}
|
|
40
|
+
// #136: the `tower-tests` row is the switch. When the row is off, say so
|
|
41
|
+
// and say how to turn it back on; never report the repository as
|
|
42
|
+
// "not configured" for a component cause.
|
|
43
|
+
const error = discovery.disabledBy
|
|
44
|
+
? `test discovery is unavailable: ${discovery.reason ?? `disabled by component ${discovery.disabledBy}`}; enable the ${discovery.disabledBy} component in the plugin page and restart DSH`
|
|
45
|
+
: discovery.error ?? 'tower.testDiscovery is not configured in the worktree package.json'
|
|
46
|
+
return { ok: false, configured: false, error, tests: [], commands: [] }
|
|
47
47
|
}
|
|
48
48
|
const receipt = discovery.receipt
|
|
49
49
|
return {
|
package/types/index.d.ts
CHANGED
|
@@ -57,6 +57,7 @@ export type ErrorCode =
|
|
|
57
57
|
| 'provider-cooldown'
|
|
58
58
|
| 'preset-missing'
|
|
59
59
|
| 'reviewer-prerequisite'
|
|
60
|
+
| 'review-disabled'
|
|
60
61
|
| 'not-authorized'
|
|
61
62
|
| 'workspace-busy'
|
|
62
63
|
| 'worker-limit'
|
|
@@ -73,6 +74,15 @@ export type ErrorCode =
|
|
|
73
74
|
| 'tool-internal'
|
|
74
75
|
// generated:vocabulary:end
|
|
75
76
|
|
|
77
|
+
/**
|
|
78
|
+
* Review policy mode (#M39): `off` (review intentionally unavailable),
|
|
79
|
+
* `optional` (available, never required; default) or `required` (a clean review
|
|
80
|
+
* bound to the branch tip is a merge blocker). The deprecated
|
|
81
|
+
* `review.requireReview` boolean normalizes to this: true -> required,
|
|
82
|
+
* false -> optional.
|
|
83
|
+
*/
|
|
84
|
+
export type ReviewMode = 'off' | 'optional' | 'required'
|
|
85
|
+
|
|
76
86
|
/** One machine-readable gate/completion blocker: a stable code plus a detail. */
|
|
77
87
|
export interface Blocker {
|
|
78
88
|
code: string
|
package/ui-bundle/locale/en.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"meta": {
|
|
3
3
|
"title": "Tower UI",
|
|
4
|
-
"description": "
|
|
4
|
+
"description": "Adds Tower cards for tool results, a compact status summary above the message box, and a short status strip, in the web client. When off: The web interface piece is not loaded: no Tower cards, status summary or status strip. The Tower host, missions, state and the board keep working."
|
|
5
5
|
}
|
|
6
6
|
}
|
package/ui-bundle/locale/zh.json
CHANGED