Feat v0.3.5 settings audit, ICD, clone + v0.3.6 roadmap
Some checks failed
CI/CD / detect-changes (pull_request) Successful in 20s
CI/CD / test-frontend (pull_request) Successful in 5s
CI/CD / test-go-pg (pull_request) Failing after 2m42s
CI/CD / test-sqlite (pull_request) Successful in 3m0s
CI/CD / build-and-deploy (pull_request) Has been skipped

v0.3.5 deliverables:
- Clone endpoint (POST /workflows/:id/clone) with deep stage copy
- 7 engine integration tests (28 total): lifecycle, branch routing,
  public entry, signoff gate, rejection, cancel, error cases
- Settings audit: staleness_timeout_hours + branch_rules UI in
  team-admin workflow editor
- ICD: fixed stale schemas, added ~20 new endpoint paths for
  instances, assignments, signoffs, public entry, clone, team roles

Roadmap additions (v0.3.6–v0.3.8):
- v0.3.6: 4 example workflow packages + demo surface proving
  public entry, branch rules, Starlark automation, signoff gates,
  HTTP webhooks end-to-end
- v0.3.7: package audit for non-chat packages
- v0.3.8: builder image + bundled packages for zero-config first run

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-03-28 12:44:37 +00:00
parent 0773c86c27
commit 752d0697e9
10 changed files with 1643 additions and 21 deletions

View File

@@ -161,12 +161,15 @@ function WorkflowEditor({ teamId, workflow, onBack }) {
e.preventDefault();
const form = e.target;
try {
await sw.api.teams.updateWorkflow(teamId, workflow.id, {
const patch = {
name: form.name.value.trim(),
description: form.description.value.trim(),
entry_mode: form.entry_mode.value,
is_active: form.is_active.checked,
});
};
const sth = form.staleness_timeout_hours.value.trim();
if (sth !== '') patch.staleness_timeout_hours = parseInt(sth, 10);
await sw.api.teams.updateWorkflow(teamId, workflow.id, patch);
sw.toast('Workflow updated', 'success');
} catch (e) { sw.toast(e.message, 'error'); }
}
@@ -230,7 +233,13 @@ function WorkflowEditor({ teamId, workflow, onBack }) {
</select>
</div>
</div>
<div class="form-group"><label>Description</label><input name="description" value=${workflow.description || ''} /></div>
<div class="form-row">
<div class="form-group" style="flex:1;"><label>Description</label><input name="description" value=${workflow.description || ''} /></div>
<div class="form-group" style="max-width:180px;"><label>Staleness (hours)</label>
<input type="number" name="staleness_timeout_hours" min="0"
value=${workflow.staleness_timeout_hours ?? ''} placeholder="disabled" />
</div>
</div>
<label class="toggle-label" style="margin:8px 0;">
<input type="checkbox" name="is_active" checked=${workflow.is_active !== false} />
<span class="toggle-track"></span><span>Active</span>
@@ -287,6 +296,9 @@ function StageForm({ stage, teams, onSave, onCancel }) {
const [assignTeam, setAssignTeam] = useState(stage?.assignment_team_id || '');
const [autoTransition, setAutoTransition] = useState(stage?.auto_transition || false);
const [sla, setSla] = useState(stage?.sla_seconds || '');
const [branchRules, setBranchRules] = useState(
stage?.branch_rules ? (typeof stage.branch_rules === 'string' ? stage.branch_rules : JSON.stringify(stage.branch_rules, null, 2)) : ''
);
// v0.3.4: stage_config fields
const sc = stage?.stage_config ? (typeof stage.stage_config === 'string' ? JSON.parse(stage.stage_config || '{}') : stage.stage_config) : {};
@@ -311,6 +323,10 @@ function StageForm({ stage, teams, onSave, onCancel }) {
reject_action: valReject || 'cancel',
};
}
let parsedBranch = null;
if (branchRules.trim()) {
try { parsedBranch = JSON.parse(branchRules); } catch { sw.toast('Invalid branch_rules JSON', 'error'); return; }
}
onSave({
name,
stage_mode: mode,
@@ -321,6 +337,7 @@ function StageForm({ stage, teams, onSave, onCancel }) {
auto_transition: autoTransition,
sla_seconds: sla ? parseInt(sla, 10) : null,
stage_config: Object.keys(stageConfig).length ? stageConfig : {},
branch_rules: parsedBranch || [],
});
}
@@ -408,6 +425,14 @@ function StageForm({ stage, teams, onSave, onCancel }) {
</div>
</div>
`}
<details style="margin-top:8px;">
<summary style="cursor:pointer;font-size:12px;color:var(--text-muted);">Branch Rules (advanced)</summary>
<div class="form-group" style="margin-top:4px;">
<textarea rows="3" value=${branchRules} onInput=${e => setBranchRules(e.target.value)}
placeholder='[{"field":"priority","op":"eq","value":"high","target_stage":"escalation"}]'
style="font-family:monospace;font-size:11px;width:100%;"></textarea>
</div>
</details>
<label class="toggle-label">
<input type="checkbox" checked=${autoTransition} onChange=${e => setAutoTransition(e.target.checked)} />
<span class="toggle-track"></span><span>Auto-advance when complete</span>