syntaur 0.70.0 → 0.72.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/.claude-plugin/plugin.json +1 -1
- package/dashboard/dist/assets/{_basePickBy-DKk6tHtk.js → _basePickBy-QfpY8LS2.js} +1 -1
- package/dashboard/dist/assets/{_baseUniq-DM-f7DWz.js → _baseUniq-J2dp0xSF.js} +1 -1
- package/dashboard/dist/assets/{arc-ZBlA3YdV.js → arc-7V3bSBdi.js} +1 -1
- package/dashboard/dist/assets/{architectureDiagram-2XIMDMQ5-BUmvtGTF.js → architectureDiagram-2XIMDMQ5-L4xF7_w_.js} +1 -1
- package/dashboard/dist/assets/{blockDiagram-WCTKOSBZ-B3qxWK6s.js → blockDiagram-WCTKOSBZ-D85LV89B.js} +1 -1
- package/dashboard/dist/assets/{c4Diagram-IC4MRINW-BEq_UJO-.js → c4Diagram-IC4MRINW-BuLOHFHk.js} +1 -1
- package/dashboard/dist/assets/channel-BPiUfFXP.js +1 -0
- package/dashboard/dist/assets/{chunk-4BX2VUAB-C-Y9ryMm.js → chunk-4BX2VUAB-PMmPIfmW.js} +1 -1
- package/dashboard/dist/assets/{chunk-55IACEB6-CGdtbsjw.js → chunk-55IACEB6-CuzXx0Ve.js} +1 -1
- package/dashboard/dist/assets/{chunk-FMBD7UC4-DllxJhUp.js → chunk-FMBD7UC4-DgbPJiW4.js} +1 -1
- package/dashboard/dist/assets/{chunk-JSJVCQXG-jjMM8O5F.js → chunk-JSJVCQXG-BIdP1PgR.js} +1 -1
- package/dashboard/dist/assets/{chunk-KX2RTZJC-B_6BPltQ.js → chunk-KX2RTZJC-Jx0-O-nW.js} +1 -1
- package/dashboard/dist/assets/{chunk-NQ4KR5QH-D0hJiXHp.js → chunk-NQ4KR5QH-CZy5XF4L.js} +1 -1
- package/dashboard/dist/assets/{chunk-QZHKN3VN-BCWo4hLS.js → chunk-QZHKN3VN-Clgnd2ni.js} +1 -1
- package/dashboard/dist/assets/{chunk-WL4C6EOR-DH_jEAwg.js → chunk-WL4C6EOR-DItFgUMd.js} +1 -1
- package/dashboard/dist/assets/classDiagram-VBA2DB6C-HDWgoi6J.js +1 -0
- package/dashboard/dist/assets/classDiagram-v2-RAHNMMFH-HDWgoi6J.js +1 -0
- package/dashboard/dist/assets/clone-BdrN_Gqo.js +1 -0
- package/dashboard/dist/assets/{cose-bilkent-S5V4N54A-5ld00TOH.js → cose-bilkent-S5V4N54A-CdvW6L_R.js} +1 -1
- package/dashboard/dist/assets/{dagre-KLK3FWXG-Cnu6eQWy.js → dagre-KLK3FWXG-200WRn9D.js} +1 -1
- package/dashboard/dist/assets/{diagram-E7M64L7V-_CBBKNP-.js → diagram-E7M64L7V-B_4QJiws.js} +1 -1
- package/dashboard/dist/assets/{diagram-IFDJBPK2-DE6WjIb1.js → diagram-IFDJBPK2-DiSnANLV.js} +1 -1
- package/dashboard/dist/assets/{diagram-P4PSJMXO-DpW7UzNK.js → diagram-P4PSJMXO-DjtHY2zM.js} +1 -1
- package/dashboard/dist/assets/{erDiagram-INFDFZHY-BD2409fE.js → erDiagram-INFDFZHY-032TAwXT.js} +1 -1
- package/dashboard/dist/assets/{flowDiagram-PKNHOUZH-1p0khhFI.js → flowDiagram-PKNHOUZH-DGNPxjzE.js} +1 -1
- package/dashboard/dist/assets/{ganttDiagram-A5KZAMGK-B2zFyA4s.js → ganttDiagram-A5KZAMGK-BjbxtEVv.js} +1 -1
- package/dashboard/dist/assets/{gitGraphDiagram-K3NZZRJ6-bH-4YH7h.js → gitGraphDiagram-K3NZZRJ6-CgFktzLw.js} +1 -1
- package/dashboard/dist/assets/{graph-BT24B6iQ.js → graph-CyuCoTKt.js} +1 -1
- package/dashboard/dist/assets/{index-Cxqr3rQB.js → index-DsFBSYQj.js} +102 -102
- package/dashboard/dist/assets/{infoDiagram-LFFYTUFH-CMzP4Hcg.js → infoDiagram-LFFYTUFH-Dav72O_U.js} +1 -1
- package/dashboard/dist/assets/{ishikawaDiagram-PHBUUO56-DMEmFC7M.js → ishikawaDiagram-PHBUUO56-RAyYfa7q.js} +1 -1
- package/dashboard/dist/assets/{journeyDiagram-4ABVD52K-CAtzYQUm.js → journeyDiagram-4ABVD52K-D_9LHvxq.js} +1 -1
- package/dashboard/dist/assets/{kanban-definition-K7BYSVSG-d1JVbtvX.js → kanban-definition-K7BYSVSG-BxawyHsy.js} +1 -1
- package/dashboard/dist/assets/{layout-BVuI38I_.js → layout-BuWGns-1.js} +1 -1
- package/dashboard/dist/assets/{linear-Bc8PGMbp.js → linear-Dml-S448.js} +1 -1
- package/dashboard/dist/assets/{mermaid.core-UtwFYLNj.js → mermaid.core-p144DJAn.js} +4 -4
- package/dashboard/dist/assets/{mindmap-definition-YRQLILUH-DHc5RCDj.js → mindmap-definition-YRQLILUH-ffX-VsIz.js} +1 -1
- package/dashboard/dist/assets/{pieDiagram-SKSYHLDU-9anIsdIA.js → pieDiagram-SKSYHLDU-CneGfBsg.js} +1 -1
- package/dashboard/dist/assets/{quadrantDiagram-337W2JSQ-FZ0D9HnU.js → quadrantDiagram-337W2JSQ-B1fvKMwB.js} +1 -1
- package/dashboard/dist/assets/{requirementDiagram-Z7DCOOCP-BkjCvH_u.js → requirementDiagram-Z7DCOOCP-B-rOBvHF.js} +1 -1
- package/dashboard/dist/assets/{sankeyDiagram-WA2Y5GQK-DzqwYHDo.js → sankeyDiagram-WA2Y5GQK-Bb_m_ROm.js} +1 -1
- package/dashboard/dist/assets/{sequenceDiagram-2WXFIKYE-BW4g5Ao-.js → sequenceDiagram-2WXFIKYE-B-H6H5KN.js} +1 -1
- package/dashboard/dist/assets/{stateDiagram-RAJIS63D-D0tKU3Z0.js → stateDiagram-RAJIS63D-CsDEjjit.js} +1 -1
- package/dashboard/dist/assets/stateDiagram-v2-FVOUBMTO-DmheKY2h.js +1 -0
- package/dashboard/dist/assets/{timeline-definition-YZTLITO2-Bu269QDX.js → timeline-definition-YZTLITO2-d0zf4ELB.js} +1 -1
- package/dashboard/dist/assets/{treemap-KZPCXAKY-BGu_rrva.js → treemap-KZPCXAKY-DZZjDZEs.js} +1 -1
- package/dashboard/dist/assets/{vennDiagram-LZ73GAT5-Cx_n5FRZ.js → vennDiagram-LZ73GAT5-CoxX9DoM.js} +1 -1
- package/dashboard/dist/assets/{xychartDiagram-JWTSCODW-BOJsKV_W.js → xychartDiagram-JWTSCODW-BN1K72KM.js} +1 -1
- package/dashboard/dist/index.html +1 -1
- package/dist/dashboard/server.js +1385 -540
- package/dist/dashboard/server.js.map +1 -1
- package/dist/index.js +2770 -1535
- package/dist/index.js.map +1 -1
- package/dist/launch/index.js +594 -109
- package/dist/launch/index.js.map +1 -1
- package/package.json +1 -1
- package/platforms/claude-code/.claude-plugin/plugin.json +1 -1
- package/platforms/claude-code/hooks/enforce-boundaries.sh +49 -16
- package/platforms/claude-code/hooks/session-cleanup.sh +4 -2
- package/platforms/claude-code/skills/add-memory/SKILL.md +3 -2
- package/platforms/claude-code/skills/add-resource/SKILL.md +3 -2
- package/platforms/claude-code/skills/bundle-worktree/SKILL.md +3 -3
- package/platforms/claude-code/skills/capture-artifacts/SKILL.md +2 -2
- package/platforms/claude-code/skills/claim-resource/SKILL.md +3 -3
- package/platforms/claude-code/skills/clear-assignment/SKILL.md +9 -27
- package/platforms/claude-code/skills/complete-assignment/SKILL.md +3 -3
- package/platforms/claude-code/skills/create-assignment/SKILL.md +2 -2
- package/platforms/claude-code/skills/extend-resource/SKILL.md +2 -2
- package/platforms/claude-code/skills/grab-assignment/SKILL.md +17 -12
- package/platforms/claude-code/skills/grab-bundle/SKILL.md +10 -6
- package/platforms/claude-code/skills/log-progress/SKILL.md +6 -4
- package/platforms/claude-code/skills/plan-assignment/SKILL.md +3 -3
- package/platforms/claude-code/skills/plan-bundle/SKILL.md +4 -3
- package/platforms/claude-code/skills/replan/SKILL.md +9 -6
- package/platforms/claude-code/skills/resume-session/SKILL.md +14 -8
- package/platforms/claude-code/skills/save-session-summary/SKILL.md +4 -4
- package/platforms/claude-code/skills/set-workspace/SKILL.md +9 -6
- package/platforms/claude-code/skills/syntaur-protocol/SKILL.md +9 -7
- package/platforms/claude-code/skills/syntaur-protocol/references/file-ownership.md +1 -1
- package/platforms/claude-code/skills/syntaur-worktree/SKILL.md +11 -7
- package/platforms/codex/.codex-plugin/plugin.json +1 -1
- package/platforms/codex/scripts/enforce-boundaries.sh +74 -12
- package/platforms/codex/scripts/session-cleanup.sh +20 -13
- package/platforms/codex/skills/add-memory/SKILL.md +3 -2
- package/platforms/codex/skills/add-resource/SKILL.md +3 -2
- package/platforms/codex/skills/bundle-worktree/SKILL.md +3 -3
- package/platforms/codex/skills/capture-artifacts/SKILL.md +2 -2
- package/platforms/codex/skills/claim-resource/SKILL.md +3 -3
- package/platforms/codex/skills/clear-assignment/SKILL.md +9 -27
- package/platforms/codex/skills/complete-assignment/SKILL.md +3 -3
- package/platforms/codex/skills/create-assignment/SKILL.md +2 -2
- package/platforms/codex/skills/extend-resource/SKILL.md +2 -2
- package/platforms/codex/skills/grab-assignment/SKILL.md +17 -12
- package/platforms/codex/skills/grab-bundle/SKILL.md +10 -6
- package/platforms/codex/skills/log-progress/SKILL.md +6 -4
- package/platforms/codex/skills/plan-assignment/SKILL.md +3 -3
- package/platforms/codex/skills/plan-bundle/SKILL.md +4 -3
- package/platforms/codex/skills/replan/SKILL.md +9 -6
- package/platforms/codex/skills/resume-session/SKILL.md +14 -8
- package/platforms/codex/skills/save-session-summary/SKILL.md +4 -4
- package/platforms/codex/skills/set-workspace/SKILL.md +9 -6
- package/platforms/codex/skills/syntaur-protocol/SKILL.md +9 -7
- package/platforms/codex/skills/syntaur-protocol/references/file-ownership.md +1 -1
- package/platforms/codex/skills/syntaur-worktree/SKILL.md +11 -7
- package/platforms/hermes/plugins/syntaur/__init__.py +29 -3
- package/platforms/hermes/plugins/syntaur/__pycache__/__init__.cpython-312.pyc +0 -0
- package/platforms/hermes/plugins/syntaur/__pycache__/boundary.cpython-312.pyc +0 -0
- package/platforms/hermes/plugins/syntaur/boundary.py +18 -9
- package/platforms/pi/extensions/syntaur/index.ts +84 -29
- package/skills/add-memory/SKILL.md +3 -2
- package/skills/add-resource/SKILL.md +3 -2
- package/skills/bundle-worktree/SKILL.md +3 -3
- package/skills/capture-artifacts/SKILL.md +2 -2
- package/skills/claim-resource/SKILL.md +3 -3
- package/skills/clear-assignment/SKILL.md +9 -27
- package/skills/complete-assignment/SKILL.md +3 -3
- package/skills/create-assignment/SKILL.md +2 -2
- package/skills/extend-resource/SKILL.md +2 -2
- package/skills/grab-assignment/SKILL.md +17 -12
- package/skills/grab-bundle/SKILL.md +10 -6
- package/skills/log-progress/SKILL.md +6 -4
- package/skills/plan-assignment/SKILL.md +3 -3
- package/skills/plan-bundle/SKILL.md +4 -3
- package/skills/replan/SKILL.md +9 -6
- package/skills/resume-session/SKILL.md +14 -8
- package/skills/save-session-summary/SKILL.md +4 -4
- package/skills/set-workspace/SKILL.md +9 -6
- package/skills/syntaur-protocol/SKILL.md +9 -7
- package/skills/syntaur-protocol/references/file-ownership.md +1 -1
- package/skills/syntaur-worktree/SKILL.md +11 -7
- package/dashboard/dist/assets/channel-fypxffzQ.js +0 -1
- package/dashboard/dist/assets/classDiagram-VBA2DB6C-1KnjQvtL.js +0 -1
- package/dashboard/dist/assets/classDiagram-v2-RAHNMMFH-1KnjQvtL.js +0 -1
- package/dashboard/dist/assets/clone-CKMabBhS.js +0 -1
- package/dashboard/dist/assets/stateDiagram-v2-FVOUBMTO-DxWQhjNO.js +0 -1
|
@@ -38,12 +38,15 @@ todos are never deleted.**
|
|
|
38
38
|
|
|
39
39
|
## Step 1: Verify there is an active assignment
|
|
40
40
|
|
|
41
|
-
|
|
42
|
-
`
|
|
43
|
-
`assignmentDir`.
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
41
|
+
The active assignment is resolved from the session's open engagement. Run
|
|
42
|
+
`syntaur session resume --json` to read the bound `assignmentSlug`,
|
|
43
|
+
`projectSlug` (may be null for standalone), and `assignmentDir`.
|
|
44
|
+
`.syntaur/context.json` is only a workspace marker — do not read the assignment
|
|
45
|
+
from it.
|
|
46
|
+
|
|
47
|
+
If there is no open engagement (no active assignment), abort with: "No active
|
|
48
|
+
assignment for this session — grab one first." Run `grab-assignment` first. Do
|
|
49
|
+
not invent values.
|
|
47
50
|
|
|
48
51
|
## Step 2: Confirm the prior plan was implemented
|
|
49
52
|
|
|
@@ -2,8 +2,9 @@
|
|
|
2
2
|
name: resume-session
|
|
3
3
|
description: >-
|
|
4
4
|
Re-orient a fresh Syntaur session on the active assignment without
|
|
5
|
-
re-reading the full transcript.
|
|
6
|
-
|
|
5
|
+
re-reading the full transcript. Resolves the active assignment from the
|
|
6
|
+
session's open engagement and loads the latest saved session summary and any
|
|
7
|
+
open handoff. Use when the user says
|
|
7
8
|
"resume", "pick up where we left off", "continue this assignment", or after
|
|
8
9
|
a compact / new session start. Symmetric counterpart to
|
|
9
10
|
`/save-session-summary`.
|
|
@@ -28,15 +29,20 @@ freely; nothing on disk changes.
|
|
|
28
29
|
- Saving the current session's progress — that's `/save-session-summary` (the
|
|
29
30
|
symmetric write side).
|
|
30
31
|
|
|
31
|
-
## Step 1: Verify there is an active
|
|
32
|
+
## Step 1: Verify there is an active assignment
|
|
32
33
|
|
|
33
34
|
Run `syntaur session resume`. The CLI:
|
|
34
35
|
|
|
35
|
-
1.
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
36
|
+
1. Resolves the active assignment from the session's OPEN engagement (the
|
|
37
|
+
assignment this session is currently bound to). `.syntaur/context.json` is
|
|
38
|
+
only a workspace marker — it identifies the repository/branch/worktree, not
|
|
39
|
+
the active assignment.
|
|
40
|
+
2. Aborts (exit 1) with a clear message when there is no open engagement —
|
|
41
|
+
"No active assignment for this session — grab one first" — telling the user
|
|
42
|
+
to run `grab-assignment`.
|
|
43
|
+
3. Otherwise resolves the assignment dir from the engagement and scans
|
|
44
|
+
`<assignmentDir>/sessions/<sid>/summary.md`, picking the most recently
|
|
45
|
+
modified.
|
|
40
46
|
4. Reads `<assignmentDir>/handoff.md` (the canonical single-file handoff per
|
|
41
47
|
assignment, managed by `complete-assignment`) and reports it if its body
|
|
42
48
|
has been written beyond the scaffolded placeholder.
|
|
@@ -26,11 +26,11 @@ mid-assignment continuity** — distinct from `handoff.md`, which is the
|
|
|
26
26
|
|
|
27
27
|
## Step 1: Load Context
|
|
28
28
|
|
|
29
|
-
|
|
29
|
+
The active assignment is resolved from the session's open engagement. Run `syntaur session resume --json` to read it.
|
|
30
30
|
|
|
31
|
-
If
|
|
31
|
+
If there is no open engagement (no active assignment), tell the user: "No active assignment for this session — grab one first." and stop. `.syntaur/context.json` is only a workspace marker; do not read the assignment from it.
|
|
32
32
|
|
|
33
|
-
|
|
33
|
+
From the resolved engagement, note:
|
|
34
34
|
- `assignmentDir` (absolute path) — required.
|
|
35
35
|
|
|
36
36
|
**Do not read the session id from `context.json` for identity.** That scalar is
|
|
@@ -86,7 +86,7 @@ printf '%s' "$BODY" | syntaur session save
|
|
|
86
86
|
# Pass --session-id <id> only to override (e.g. you already have $CLAUDE_CODE_SESSION_ID).
|
|
87
87
|
```
|
|
88
88
|
|
|
89
|
-
Resolves the active assignment from
|
|
89
|
+
Resolves the active assignment from the session's open engagement (or pass
|
|
90
90
|
`--assignment <slug> [--project <slug>]`). `--session-id` now defaults to the
|
|
91
91
|
**resolved** session (env → process tree → transcript), falling back to the
|
|
92
92
|
`context.json` hint only as a last resort — so a co-tenant that clobbered the
|
|
@@ -35,11 +35,14 @@ implementation code until workspace fields are set.
|
|
|
35
35
|
|
|
36
36
|
## Step 1: Resolve the assignment file
|
|
37
37
|
|
|
38
|
-
|
|
39
|
-
`
|
|
38
|
+
The active assignment is resolved from the session's open engagement — `syntaur
|
|
39
|
+
workspace set` (Step 3) targets it automatically. `.syntaur/context.json` is
|
|
40
|
+
only a workspace marker; do not read the assignment from it.
|
|
40
41
|
|
|
41
|
-
If no
|
|
42
|
-
first."
|
|
42
|
+
If there is no open engagement (no active assignment), the CLI aborts with "No
|
|
43
|
+
active assignment for this session — grab one first." Run `grab-assignment`
|
|
44
|
+
first, or pass `--assignment <slug> [--project <slug>]` to target one
|
|
45
|
+
explicitly.
|
|
43
46
|
|
|
44
47
|
## Step 2: Gather inputs
|
|
45
48
|
|
|
@@ -68,8 +71,8 @@ syntaur workspace set \
|
|
|
68
71
|
--parent-branch <parent>
|
|
69
72
|
```
|
|
70
73
|
|
|
71
|
-
Targets the active assignment from
|
|
72
|
-
`--assignment <slug> [--project <slug>]` to target one explicitly. The command
|
|
74
|
+
Targets the active assignment from the session's open engagement by default;
|
|
75
|
+
pass `--assignment <slug> [--project <slug>]` to target one explicitly. The command
|
|
73
76
|
does the whole safe write in one atomic step:
|
|
74
77
|
|
|
75
78
|
- **Pre-write validation** — runs the same checks as `syntaur doctor
|
|
@@ -32,7 +32,7 @@ Respect file ownership boundaries. The Claude Code and Codex plugins enforce the
|
|
|
32
32
|
- `~/.syntaur/projects/<project>/resources/<slug>.md`
|
|
33
33
|
- `~/.syntaur/projects/<project>/memories/<slug>.md`
|
|
34
34
|
3. **Workspace files** inside the assignment's configured `workspace.worktreePath` / `workspace.repository`.
|
|
35
|
-
4. **
|
|
35
|
+
4. **Workspace marker:** `.syntaur/context.json` in the current working directory (repository/branch/worktree markers plus legacy session/lease bookkeeping — not the active-assignment source of truth).
|
|
36
36
|
|
|
37
37
|
### Files written only via CLI (never edit directly)
|
|
38
38
|
|
|
@@ -51,16 +51,18 @@ Per-project `agent.md` / `claude.md` do NOT exist in protocol v2.0. Agent-level
|
|
|
51
51
|
|
|
52
52
|
## Current Assignment Context
|
|
53
53
|
|
|
54
|
-
|
|
54
|
+
The **active assignment** is resolved from the session's OPEN engagement — the assignment this session is currently bound to (established by `syntaur track-session`). It is NOT read from `.syntaur/context.json`. To learn the active assignment, run `syntaur session resume` (or `--json`); to bind a different one, `grab-assignment` (which calls `track-session`).
|
|
55
55
|
|
|
56
|
-
- `projectSlug`
|
|
57
|
-
|
|
58
|
-
- `
|
|
59
|
-
- `
|
|
56
|
+
`.syntaur/context.json` is a WORKSPACE MARKER file — it identifies the workspace directory and carries legacy session and resource-lease bookkeeping. It is NOT authoritative for the active assignment. Read it for workspace markers and leases; do NOT treat `projectSlug` / `assignmentSlug` / `assignmentDir` as the active-assignment source of truth (any such scalars are non-authoritative legacy hints). Fields you may see:
|
|
57
|
+
|
|
58
|
+
- `repository` — workspace repository (path or remote URL)
|
|
59
|
+
- `branch` — workspace branch, if known
|
|
60
|
+
- `worktreePath` — absolute path to the worktree, if this workspace is a worktree
|
|
60
61
|
- `workspaceRoot` — absolute path to the code workspace
|
|
61
|
-
- `sessionId` — real agent-runtime session id (never a synthesized UUID)
|
|
62
|
+
- `sessionId` — real agent-runtime session id (legacy hint; never a synthesized UUID, and a co-tenant can clobber it — not authoritative for identity)
|
|
62
63
|
- `transcriptPath` — absolute path to the agent's rollout/transcript file, if known
|
|
63
64
|
- `leases` — array of active resource-lease records (managed by `/claim-resource` and `/release-resource`). Entry shape: `{ lease_id, inventory_slug, member_id, expires_at, metadata, claimed_at }`. Leases are NOT auto-released on session end or assignment completion in v1 — call `/release-resource` explicitly (or let the TTL expire).
|
|
65
|
+
- `bundleId` and related bundle fields — present when this workspace is bound to a todo bundle (managed by the bundle skills).
|
|
64
66
|
|
|
65
67
|
## Required Reading Order
|
|
66
68
|
|
|
@@ -64,4 +64,4 @@ When working on code (not protocol files), you may write to files within the wor
|
|
|
64
64
|
|
|
65
65
|
- `workspace.worktreePath` or `workspace.repository` defines your code root.
|
|
66
66
|
- You may create and edit source files within that workspace.
|
|
67
|
-
- The `.syntaur/context.json`
|
|
67
|
+
- The `.syntaur/context.json` workspace-marker file in your working directory is also writable (merge, don't overwrite — the platform SessionStart hook may have populated `sessionId` and `transcriptPath`). It marks the workspace (repository/branch/worktree); it is not the active-assignment source of truth — the active assignment resolves from the session's open engagement.
|
|
@@ -23,8 +23,9 @@ operations:
|
|
|
23
23
|
transactionally — rolls back the worktree on write failure).
|
|
24
24
|
3. `syntaur assign` + `syntaur start` — claim the assignment for this agent
|
|
25
25
|
and transition to `in_progress`.
|
|
26
|
-
4. Write `.syntaur/context.json` inside the new worktree
|
|
27
|
-
|
|
26
|
+
4. Write `.syntaur/context.json` (a workspace marker) inside the new worktree,
|
|
27
|
+
then `syntaur track-session` to bind the session's engagement to the
|
|
28
|
+
assignment.
|
|
28
29
|
|
|
29
30
|
## When NOT to use this skill
|
|
30
31
|
|
|
@@ -82,12 +83,15 @@ syntaur start <assignment> --project <project-slug> # only if status was pendi
|
|
|
82
83
|
Skip `start` for any non-`pending` status — never rewind a `review`,
|
|
83
84
|
`completed`, or `failed` assignment.
|
|
84
85
|
|
|
85
|
-
## Step 5:
|
|
86
|
+
## Step 5: Write the workspace marker
|
|
86
87
|
|
|
87
|
-
`cd` into the new worktree path. Write
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
88
|
+
`cd` into the new worktree path. Write `<worktreePath>/.syntaur/context.json`
|
|
89
|
+
mirroring the WORKSPACE-MARKER format produced by `/grab-assignment`:
|
|
90
|
+
`repository`, `branch`, `worktreePath`, `workspaceRoot`, `grabbedAt`, plus
|
|
91
|
+
`sessionId` / `transcriptPath` when known. Do NOT write `projectSlug` /
|
|
92
|
+
`assignmentSlug` / `assignmentDir` / `projectDir` / `title` — context.json is a
|
|
93
|
+
workspace marker, not the active-assignment source. The assignment binds in the
|
|
94
|
+
next step via the session's engagement (`track-session`).
|
|
91
95
|
|
|
92
96
|
If the runtime exposes a real session id (e.g. Claude Code's
|
|
93
97
|
`~/.claude/sessions/`), include it. Otherwise omit `sessionId` entirely; the
|
|
@@ -57,6 +57,24 @@ def _dashboard_port():
|
|
|
57
57
|
return "4800"
|
|
58
58
|
|
|
59
59
|
|
|
60
|
+
def _resolve_boundary(cwd, session_id=None):
|
|
61
|
+
"""Resolve the write boundary {assignmentDir, projectDir, workspaceRoot} from
|
|
62
|
+
the session's OPEN engagement via the CLI. context.json is a workspace marker
|
|
63
|
+
now — the active assignment lives on the engagement, so the enforcer can no
|
|
64
|
+
longer read assignmentDir/projectDir straight from the file. Returns {} on any
|
|
65
|
+
failure, which makes is_write_allowed fall back to workspace-only enforcement
|
|
66
|
+
(never fail-open)."""
|
|
67
|
+
argv = ["syntaur", "session", "boundary", "--json", "--cwd", cwd]
|
|
68
|
+
if session_id:
|
|
69
|
+
argv += ["--session-id", session_id]
|
|
70
|
+
try:
|
|
71
|
+
out = subprocess.run(argv, capture_output=True, text=True, timeout=3)
|
|
72
|
+
data = json.loads(out.stdout or "{}")
|
|
73
|
+
return data if isinstance(data, dict) else {}
|
|
74
|
+
except Exception:
|
|
75
|
+
return {}
|
|
76
|
+
|
|
77
|
+
|
|
60
78
|
def _log_violation(reason):
|
|
61
79
|
try:
|
|
62
80
|
sys.stderr.write("[syntaur] " + reason + "\n")
|
|
@@ -80,7 +98,14 @@ def _on_pre_tool_call(tool_name=None, args=None, task_id=None, **kwargs):
|
|
|
80
98
|
cwd = os.getcwd()
|
|
81
99
|
ctx = boundary.load_context(cwd)
|
|
82
100
|
if not ctx:
|
|
83
|
-
return None
|
|
101
|
+
return None # no .syntaur/context.json → not in a workspace → allow
|
|
102
|
+
# Resolve the real assignment/project boundary from the session's open
|
|
103
|
+
# engagement and merge it over the workspace markers in context.json.
|
|
104
|
+
# If nothing resolves, is_write_allowed enforces workspace-only.
|
|
105
|
+
resolved = _resolve_boundary(cwd, ctx.get("sessionId"))
|
|
106
|
+
for k in ("assignmentDir", "projectDir", "workspaceRoot"):
|
|
107
|
+
if resolved.get(k):
|
|
108
|
+
ctx[k] = resolved[k]
|
|
84
109
|
abs_path = path if os.path.isabs(path) else os.path.join(cwd, path)
|
|
85
110
|
context_file = os.path.join(cwd, ".syntaur", "context.json")
|
|
86
111
|
allowed, reason = boundary.is_write_allowed(abs_path, ctx, context_file)
|
|
@@ -129,8 +154,9 @@ def _make_command_handler(cmd):
|
|
|
129
154
|
except Exception as exc:
|
|
130
155
|
return "Failed to run syntaur %s: %s" % (" ".join(cmd["argv"]), exc)
|
|
131
156
|
return (
|
|
132
|
-
'Follow the Syntaur "%s" skill (installed via skills). It
|
|
133
|
-
"assignment
|
|
157
|
+
'Follow the Syntaur "%s" skill (installed via skills). It resolves the active '
|
|
158
|
+
"assignment from the session's open engagement (.syntaur/context.json is a "
|
|
159
|
+
"workspace marker)." % cmd["skill"]
|
|
134
160
|
)
|
|
135
161
|
|
|
136
162
|
return handler
|
|
Binary file
|
|
Binary file
|
|
@@ -18,7 +18,16 @@ def _expand_home(p):
|
|
|
18
18
|
|
|
19
19
|
|
|
20
20
|
def load_context(cwd):
|
|
21
|
-
"""Read <cwd>/.syntaur/context.json; return
|
|
21
|
+
"""Read <cwd>/.syntaur/context.json; return its WORKSPACE-MARKER fields, or
|
|
22
|
+
None when absent/unparseable (caller treats None as "not in a workspace").
|
|
23
|
+
|
|
24
|
+
Deliberately does NOT surface the demoted assignment scalars
|
|
25
|
+
(assignmentDir/projectDir): the active assignment/project dirs are resolved
|
|
26
|
+
from the session's open engagement by the caller and merged in. Surfacing
|
|
27
|
+
stale legacy scalars here would let them leak through on a boundary-resolution
|
|
28
|
+
failure, so the {}-failure path could wrongly allow writes under a stale dir
|
|
29
|
+
instead of staying workspace-only.
|
|
30
|
+
"""
|
|
22
31
|
path = os.path.join(cwd, ".syntaur", "context.json")
|
|
23
32
|
try:
|
|
24
33
|
with open(path, "r", encoding="utf-8") as fh:
|
|
@@ -37,8 +46,6 @@ def load_context(cwd):
|
|
|
37
46
|
return _expand_home(v) if v else None
|
|
38
47
|
|
|
39
48
|
return {
|
|
40
|
-
"assignmentDir": home("assignmentDir"),
|
|
41
|
-
"projectDir": home("projectDir"),
|
|
42
49
|
"workspaceRoot": home("workspaceRoot"),
|
|
43
50
|
"sessionId": s("sessionId"),
|
|
44
51
|
"projectSlug": s("projectSlug"),
|
|
@@ -67,12 +74,14 @@ def is_write_allowed(abs_file_path, ctx, context_file_abs=None):
|
|
|
67
74
|
resources/memories (excluding derived `_*` files), equal to the context file,
|
|
68
75
|
or under the workspace root. Otherwise blocked.
|
|
69
76
|
"""
|
|
70
|
-
#
|
|
71
|
-
#
|
|
72
|
-
#
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
77
|
+
# context.json is a WORKSPACE MARKER now (the assignment scalars were
|
|
78
|
+
# demoted); the active assignment resolves from the session's open engagement.
|
|
79
|
+
# When the caller cannot supply assignmentDir/projectDir (no engagement
|
|
80
|
+
# resolved), we do NOT fail open — we enforce WORKSPACE-ONLY via the
|
|
81
|
+
# workspaceRoot marker below. Each dir check is guarded for absence, so a
|
|
82
|
+
# missing assignmentDir/projectDir simply narrows the allowlist rather than
|
|
83
|
+
# disabling it. (Assignment-record writes under ~/.syntaur go through the CLI,
|
|
84
|
+
# not the agent's file tools, so they never hit this hook.)
|
|
76
85
|
f = _norm(abs_file_path)
|
|
77
86
|
|
|
78
87
|
if ctx.get("assignmentDir") and _is_under(f, ctx["assignmentDir"]):
|
|
@@ -13,18 +13,30 @@
|
|
|
13
13
|
// pi.registerCommand(name, { description, handler }) }`. The `tool_call` handler
|
|
14
14
|
// returns `{ block: true, reason }` to DENY a tool call; any other return allows.
|
|
15
15
|
|
|
16
|
-
import {
|
|
16
|
+
import { spawnSync } from 'node:child_process';
|
|
17
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
17
18
|
import { homedir } from 'node:os';
|
|
18
19
|
import { isAbsolute, resolve, sep } from 'node:path';
|
|
19
20
|
|
|
20
21
|
export interface SyntaurContext {
|
|
21
|
-
|
|
22
|
-
projectDir?: string;
|
|
22
|
+
/** Workspace marker; the only path scalar still authoritative in context.json. */
|
|
23
23
|
workspaceRoot?: string;
|
|
24
24
|
sessionId?: string;
|
|
25
25
|
projectSlug?: string;
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
+
/**
|
|
29
|
+
* The resolved write boundary for a session — mirrors the CLI's
|
|
30
|
+
* `syntaur session boundary --json` contract. The assignment/project dirs come
|
|
31
|
+
* from the session's OPEN engagement (NOT context.json, whose assignment scalars
|
|
32
|
+
* were demoted). `workspaceRoot` is the context.json marker.
|
|
33
|
+
*/
|
|
34
|
+
export interface SyntaurBoundary {
|
|
35
|
+
assignmentDir?: string;
|
|
36
|
+
projectDir?: string;
|
|
37
|
+
workspaceRoot?: string;
|
|
38
|
+
}
|
|
39
|
+
|
|
28
40
|
/** Expand a leading `~` to the home dir (the only expansion the bash hooks do). */
|
|
29
41
|
function expandHome(p: string): string {
|
|
30
42
|
if (p === '~') return homedir();
|
|
@@ -32,7 +44,12 @@ function expandHome(p: string): string {
|
|
|
32
44
|
return p;
|
|
33
45
|
}
|
|
34
46
|
|
|
35
|
-
/**
|
|
47
|
+
/**
|
|
48
|
+
* Read `<cwd>/.syntaur/context.json`. context.json is now a WORKSPACE MARKER —
|
|
49
|
+
* this only surfaces `workspaceRoot` / `sessionId` / `projectSlug`. The active
|
|
50
|
+
* assignment/project dirs live on the session's engagement (resolveBoundary).
|
|
51
|
+
* null when absent/unparseable.
|
|
52
|
+
*/
|
|
36
53
|
export function loadContext(cwd: string): SyntaurContext | null {
|
|
37
54
|
const file = resolve(cwd, '.syntaur', 'context.json');
|
|
38
55
|
let raw: string;
|
|
@@ -49,18 +66,50 @@ export function loadContext(cwd: string): SyntaurContext | null {
|
|
|
49
66
|
}
|
|
50
67
|
const str = (v: unknown): string | undefined =>
|
|
51
68
|
typeof v === 'string' && v.length > 0 ? v : undefined;
|
|
52
|
-
const assignmentDir = str(data.assignmentDir);
|
|
53
|
-
const projectDir = str(data.projectDir);
|
|
54
69
|
const workspaceRoot = str(data.workspaceRoot);
|
|
55
70
|
return {
|
|
56
|
-
assignmentDir: assignmentDir ? expandHome(assignmentDir) : undefined,
|
|
57
|
-
projectDir: projectDir ? expandHome(projectDir) : undefined,
|
|
58
71
|
workspaceRoot: workspaceRoot ? expandHome(workspaceRoot) : undefined,
|
|
59
72
|
sessionId: str(data.sessionId),
|
|
60
73
|
projectSlug: str(data.projectSlug),
|
|
61
74
|
};
|
|
62
75
|
}
|
|
63
76
|
|
|
77
|
+
/** True iff `<cwd>/.syntaur/context.json` exists (the Syntaur-workspace marker). */
|
|
78
|
+
export function hasContextFile(cwd: string): boolean {
|
|
79
|
+
return existsSync(resolve(cwd, '.syntaur', 'context.json'));
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Resolve the write boundary from the session's OPEN engagement by shelling out
|
|
84
|
+
* to the same CLI the bash hooks call: `syntaur session boundary --json`. Returns
|
|
85
|
+
* a {} boundary (all-empty → workspace-only enforcement) on ANY failure — the CLI
|
|
86
|
+
* itself never throws, and a missing/old CLI must NOT fail open. Paths are
|
|
87
|
+
* `~`-expanded for parity with the bash hooks.
|
|
88
|
+
*/
|
|
89
|
+
export function resolveBoundary(cwd: string, sessionId?: string): SyntaurBoundary {
|
|
90
|
+
const args = ['session', 'boundary', '--json'];
|
|
91
|
+
if (sessionId) args.push('--session-id', sessionId);
|
|
92
|
+
let parsed: Record<string, unknown> = {};
|
|
93
|
+
try {
|
|
94
|
+
const r = spawnSync('syntaur', args, { cwd, encoding: 'utf-8', timeout: 5000 });
|
|
95
|
+
if (r.status === 0 && typeof r.stdout === 'string' && r.stdout.trim().length > 0) {
|
|
96
|
+
parsed = JSON.parse(r.stdout) as Record<string, unknown>;
|
|
97
|
+
}
|
|
98
|
+
} catch {
|
|
99
|
+
/* CLI missing / spawn error / unparseable → empty boundary (workspace-only) */
|
|
100
|
+
}
|
|
101
|
+
const str = (v: unknown): string | undefined =>
|
|
102
|
+
typeof v === 'string' && v.length > 0 ? v : undefined;
|
|
103
|
+
const assignmentDir = str(parsed.assignmentDir);
|
|
104
|
+
const projectDir = str(parsed.projectDir);
|
|
105
|
+
const workspaceRoot = str(parsed.workspaceRoot);
|
|
106
|
+
return {
|
|
107
|
+
assignmentDir: assignmentDir ? expandHome(assignmentDir) : undefined,
|
|
108
|
+
projectDir: projectDir ? expandHome(projectDir) : undefined,
|
|
109
|
+
workspaceRoot: workspaceRoot ? expandHome(workspaceRoot) : undefined,
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
|
|
64
113
|
/** True if `child` is strictly under `parent` (mirrors the bash `"$X"/*` test). */
|
|
65
114
|
function isUnder(child: string, parent: string): boolean {
|
|
66
115
|
if (!parent) return false;
|
|
@@ -76,31 +125,31 @@ function basename(p: string): string {
|
|
|
76
125
|
}
|
|
77
126
|
|
|
78
127
|
/**
|
|
79
|
-
* Decide whether a write to `absFilePath` is allowed under the
|
|
80
|
-
* Mirrors `platforms/claude-code/hooks/enforce-boundaries.sh
|
|
81
|
-
* - allow under assignmentDir
|
|
128
|
+
* Decide whether a write to `absFilePath` is allowed under the resolved boundary.
|
|
129
|
+
* Mirrors `platforms/claude-code/hooks/enforce-boundaries.sh`:
|
|
130
|
+
* - allow under assignmentDir (if resolved)
|
|
82
131
|
* - allow under projectDir/resources/ and projectDir/memories/ EXCEPT derived `_*` files
|
|
83
132
|
* - allow the `.syntaur/context.json` file itself (caller passes cwd-resolved path)
|
|
84
133
|
* - allow under workspaceRoot (if set)
|
|
85
134
|
* - otherwise block
|
|
135
|
+
*
|
|
136
|
+
* CRITICAL: no fail-open. When the boundary has no assignment/project (no open
|
|
137
|
+
* engagement) but a workspace is known, only the workspace-root (and context
|
|
138
|
+
* file) gates can match → WORKSPACE-ONLY enforcement. Every `isUnder` check is
|
|
139
|
+
* already empty-safe (`isUnder` returns false for an empty parent).
|
|
86
140
|
*/
|
|
87
141
|
export function isWriteAllowed(
|
|
88
142
|
absFilePath: string,
|
|
89
|
-
|
|
143
|
+
boundary: SyntaurBoundary,
|
|
90
144
|
contextFileAbs?: string,
|
|
91
145
|
): { allowed: boolean; reason?: string } {
|
|
92
|
-
// Parity with the bash hook (enforce-boundaries.sh:69): enforcement requires
|
|
93
|
-
// BOTH assignmentDir and projectDir. If either is missing (standalone / old /
|
|
94
|
-
// partially-written context), fail OPEN — allow everything.
|
|
95
|
-
if (!ctx.assignmentDir || !ctx.projectDir) return { allowed: true };
|
|
96
|
-
|
|
97
146
|
const file = resolve(absFilePath);
|
|
98
147
|
|
|
99
|
-
if (
|
|
148
|
+
if (boundary.assignmentDir && isUnder(file, boundary.assignmentDir)) return { allowed: true };
|
|
100
149
|
|
|
101
|
-
if (
|
|
102
|
-
const resourcesDir = resolve(
|
|
103
|
-
const memoriesDir = resolve(
|
|
150
|
+
if (boundary.projectDir) {
|
|
151
|
+
const resourcesDir = resolve(boundary.projectDir, 'resources');
|
|
152
|
+
const memoriesDir = resolve(boundary.projectDir, 'memories');
|
|
104
153
|
for (const dir of [resourcesDir, memoriesDir]) {
|
|
105
154
|
if (isUnder(file, dir) && !basename(file).startsWith('_')) return { allowed: true };
|
|
106
155
|
}
|
|
@@ -108,12 +157,14 @@ export function isWriteAllowed(
|
|
|
108
157
|
|
|
109
158
|
if (contextFileAbs && file === resolve(contextFileAbs)) return { allowed: true };
|
|
110
159
|
|
|
111
|
-
if (
|
|
160
|
+
if (boundary.workspaceRoot && isUnder(file, boundary.workspaceRoot)) return { allowed: true };
|
|
112
161
|
|
|
113
|
-
const reason =
|
|
114
|
-
`Syntaur write boundary violation: cannot write to '${file}'. Allowed: assignment dir ` +
|
|
115
|
-
|
|
116
|
-
|
|
162
|
+
const reason = boundary.assignmentDir
|
|
163
|
+
? `Syntaur write boundary violation: cannot write to '${file}'. Allowed: assignment dir ` +
|
|
164
|
+
`(${boundary.assignmentDir}), project resources/memories, workspace ` +
|
|
165
|
+
`(${boundary.workspaceRoot ?? 'n/a'}).`
|
|
166
|
+
: `Syntaur write boundary violation: cannot write to '${file}'. No active assignment for ` +
|
|
167
|
+
`this session — writes are restricted to the workspace (${boundary.workspaceRoot ?? 'n/a'}).`;
|
|
117
168
|
return { allowed: false, reason };
|
|
118
169
|
}
|
|
119
170
|
|
|
@@ -229,11 +280,16 @@ export default function activate(pi: {
|
|
|
229
280
|
const path = extractWritePath(e.toolName, e.input);
|
|
230
281
|
if (!path) return; // not a write → allow
|
|
231
282
|
const cwd = process.cwd();
|
|
283
|
+
// context.json presence is the Syntaur-workspace marker. Absent → not a
|
|
284
|
+
// Syntaur workspace → allow (unchanged). Present → enforce. The assignment
|
|
285
|
+
// boundary is resolved from the session's OPEN engagement via the CLI — NOT
|
|
286
|
+
// the demoted context.json scalars. No engagement → workspace-only.
|
|
287
|
+
if (!hasContextFile(cwd)) return; // not a Syntaur workspace → allow
|
|
232
288
|
const ctx = loadContext(cwd);
|
|
233
|
-
|
|
289
|
+
const boundary = resolveBoundary(cwd, ctx?.sessionId);
|
|
234
290
|
const abs = resolveAbs(path, cwd);
|
|
235
291
|
const contextFileAbs = resolve(cwd, '.syntaur', 'context.json');
|
|
236
|
-
const { allowed, reason } = isWriteAllowed(abs,
|
|
292
|
+
const { allowed, reason } = isWriteAllowed(abs, boundary, contextFileAbs);
|
|
237
293
|
if (!allowed) return { block: true, reason };
|
|
238
294
|
return undefined;
|
|
239
295
|
});
|
|
@@ -249,7 +305,6 @@ export default function activate(pi: {
|
|
|
249
305
|
description: cmd.description,
|
|
250
306
|
handler: async (_args: string, ctx: unknown) => {
|
|
251
307
|
if (cmd.kind === 'passthrough' && cmd.argv) {
|
|
252
|
-
const { spawnSync } = await import('node:child_process');
|
|
253
308
|
const r = spawnSync('syntaur', cmd.argv, { encoding: 'utf-8' });
|
|
254
309
|
notify(ctx, (r.stdout || '') + (r.stderr || '') || `ran: syntaur ${cmd.argv.join(' ')}`);
|
|
255
310
|
return;
|
|
@@ -32,8 +32,9 @@ file-ownership protocol.
|
|
|
32
32
|
|
|
33
33
|
## Step 1: Resolve project
|
|
34
34
|
|
|
35
|
-
If
|
|
36
|
-
|
|
35
|
+
If the session has an open engagement with an active assignment (`syntaur
|
|
36
|
+
session resume --json`), default to its `projectSlug`. Otherwise ask the user
|
|
37
|
+
which project to add the memory to.
|
|
37
38
|
|
|
38
39
|
## Step 2: Gather inputs
|
|
39
40
|
|
|
@@ -30,8 +30,9 @@ CLI-managed file per the file-ownership protocol).
|
|
|
30
30
|
|
|
31
31
|
## Step 1: Resolve project
|
|
32
32
|
|
|
33
|
-
If
|
|
34
|
-
|
|
33
|
+
If the session has an open engagement with an active assignment (`syntaur
|
|
34
|
+
session resume --json`), default to its `projectSlug`. Otherwise ask the user
|
|
35
|
+
which project to add the resource to.
|
|
35
36
|
|
|
36
37
|
## Step 2: Gather inputs
|
|
37
38
|
|
|
@@ -45,9 +45,9 @@ The computed worktree path is **always**
|
|
|
45
45
|
|
|
46
46
|
## Step 2: Pre-flight
|
|
47
47
|
|
|
48
|
-
- Confirm `.syntaur/context.json` is a bundle context (has `bundleId
|
|
49
|
-
|
|
50
|
-
user to `/grab-bundle <id>` first.
|
|
48
|
+
- Confirm `.syntaur/context.json` is a bundle context (has `bundleId`). If the
|
|
49
|
+
session is instead bound to an assignment (an open engagement) with no
|
|
50
|
+
`bundleId`, stop and tell the user to `/grab-bundle <id>` first.
|
|
51
51
|
- Confirm `<repository>/.git` exists.
|
|
52
52
|
- Confirm the branch does NOT already exist. If it does, the CLI will
|
|
53
53
|
surface a `GitWorktreeError` — surface that verbatim.
|
|
@@ -17,7 +17,7 @@ Attach proof to your work. Reviewers should be able to scroll a single page (`pr
|
|
|
17
17
|
|
|
18
18
|
## Input
|
|
19
19
|
|
|
20
|
-
No arguments.
|
|
20
|
+
No arguments. The active assignment is resolved from the session's open engagement (`.syntaur/context.json` is only a workspace marker, not the assignment source).
|
|
21
21
|
|
|
22
22
|
## Step 1: Decide whether to capture
|
|
23
23
|
|
|
@@ -60,7 +60,7 @@ Rules:
|
|
|
60
60
|
- All other kinds (`screenshot`, `video`, `asciinema`) require `--file`. The CLI rejects nonexistent / non-file paths.
|
|
61
61
|
- `--criterion <index>` is optional — pass the **0-based** index into the `## Acceptance Criteria` checklist when you want to anchor the artifact to a specific criterion.
|
|
62
62
|
- `--transcribe` is video-only and writes a sibling `<id>.transcript.md` (requires `ELEVENLABS_API_KEY` + `ffmpeg`). `proof build` renders the transcript next to the player; clicking a phrase seeks the video.
|
|
63
|
-
- If
|
|
63
|
+
- If this session has an open engagement (active assignment), the positional target argument is unnecessary — the CLI resolves the target from the engagement. Otherwise pass `--project <slug> <assignment-slug>` or a bare assignment UUID.
|
|
64
64
|
|
|
65
65
|
The CLI copies the file (if any) under `<assignmentDir>/proof/<criterion|untagged>/<id>.<ext>` and inserts a row in `~/.syntaur/syntaur.db`. Output prints the artifact id and absolute path.
|
|
66
66
|
|
|
@@ -29,15 +29,15 @@ Examples:
|
|
|
29
29
|
|
|
30
30
|
Extract `<inventory_slug>` (required, first positional), plus optional `--ttl <duration>` and `--for <tag>`.
|
|
31
31
|
|
|
32
|
-
### Step 2: Pre-check
|
|
32
|
+
### Step 2: Pre-check workspace context
|
|
33
33
|
|
|
34
|
-
Read `.syntaur/context.json` from the current working directory. If the file is missing, OR
|
|
34
|
+
Read `.syntaur/context.json` (a workspace marker) from the current working directory. If the file is missing, OR carries no workspace markers (`repository`/`branch`/`worktreePath`/`workspaceRoot`) and no `sessionId`, abort with:
|
|
35
35
|
|
|
36
36
|
> "No active Syntaur context in this workspace. Grab an assignment first (`/grab-assignment <project> <slug>`) or restart the session so the SessionStart hook can populate `.syntaur/context.json`."
|
|
37
37
|
|
|
38
38
|
This guard exists because writing a leases-only context file would trip `syntaur doctor`'s workspace check.
|
|
39
39
|
|
|
40
|
-
If `--for` was not provided, derive its value from the
|
|
40
|
+
If `--for` was not provided, derive its value: prefer the active assignment's slug from the session's open engagement (`syntaur session resume --json`); otherwise fall back to the `sessionId` from the workspace marker.
|
|
41
41
|
|
|
42
42
|
### Step 3: Run the claim CLI
|
|
43
43
|
|