@specset/cli 1.0.0 → 2.0.1

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.
@@ -18,8 +18,6 @@ Maintenance has three layers:
18
18
  - **MaintenancePlan** — a schedule on exactly one Product, Asset, Location, or System. Product plans fan out to installed assets. Its `tiers` are independent schedule items that pair a Procedure with a calendar cadence, meter trigger, or both.
19
19
  - **WorkOrder** — a concrete occurrence or ad-hoc job. Generated work orders snapshot their checklist fields, so history remains stable when a Procedure changes.
20
20
 
21
- `MaintenanceTask` and its completion API no longer exist. Use Procedure, MaintenancePlan, and WorkOrder operations below.
22
-
23
21
  ## Discover Before Mutating
24
22
 
25
23
  Input objects evolve, especially checklist fields and plan tiers. Introspect the relevant input before composing a mutation:
@@ -164,9 +162,158 @@ Other lifecycle operations:
164
162
 
165
163
  ## Entity Documents and Documentation
166
164
 
167
- Location, Product, Company, System, and Warranty link project Documents through entity-specific role-tagged pivots. Read `<entity>Documents(<entity>Id)`, then call `set<Entity>DocumentRoles(<entity>Id, documentId, roles)` after introspecting that entity's role enum. An empty role list removes the pivot and may delete the underlying Document when it becomes orphaned. Asset is the exception: it exposes `assetDocuments(assetId)` and `deleteAssetDocument(id)`, but no set-roles mutation.
165
+ Asset, Location, Product, Company, System, and Warranty link project Documents through entity-specific role-tagged pivots. Read `<entity>Documents(<entity>Id)`, then call `set<Entity>DocumentRoles(<entity>Id, documentId, roles)` after introspecting that entity's role enum (`AssetDocumentRole`, `ProductDocumentRole`, `SystemDocumentRole`, `LocationDocumentRole`, `CompanyDocumentRole`, `WarrantyDocumentRole`). The roles array is replaced atomically; an empty role list removes the pivot and may delete the underlying Document when it becomes orphaned. Asset also exposes `deleteAssetDocument(id)`.
166
+
167
+ Attaching a new file is a three-step flow — the same Document can then be linked to several entities (a family IOM shared by every product in the family):
168
+
169
+ ```bash
170
+ CLOUD_FILE=$(specset files upload ./aaon-rn-iom.pdf --type application/pdf --json | jq -r .id)
171
+ specset api --query "mutation { createDocumentsFromCloudFiles(cloudFileIds: [\"$CLOUD_FILE\"], projectId: \"<project-id>\") { id title } }"
172
+ specset api --query 'mutation { updateDocument(id: "<document-id>", title: "AAON RN Series — IOM") { id } }' # title drives lists and PDF bookmarks; the default is the filename
173
+ specset api --query 'mutation { setProductDocumentRoles(productId: "<product-id>", documentId: "<document-id>", roles: [OperationManual, MaintenanceManual]) { id } }'
174
+ ```
175
+
176
+ Existing project Documents work too — a submittal attachment's `document { id }` (from `paginatedSubmittals … attachments { document { id title } }`) can be linked directly as `SubmittalPackage`/`Datasheet`; the only check is that the Document belongs to the same project. To bind a slice of a large file, `extractPagesFromFileResource(id: <fileResourceId>, pages: [3, 4, 5])` returns a new CloudFile to feed into `createDocumentsFromCloudFiles`. Only PDF files merge into the O&M package export; images and other types are listed as references.
177
+
178
+ Asset, Location, Product, Company, and System also have markdown documentation blocks. `update<Entity>Documentation(id, markdown)` replaces the block; `mark<Entity>DocumentationComplete(id)` attests that it is current. Any later create/update/link on the entity (a new document pivot, a warranty document, a field edit) marks the documentation stale again, so when seeding a graph write documentation **last**. Calling `mark<Entity>DocumentationComplete` with an empty block sets the state to `NotApplicable`, which the package readiness report accepts — use it for entities that don't warrant prose rather than writing filler.
179
+
180
+ ### Writing documentation that reads well in the O&M package export
181
+
182
+ The rules below are the same ones the in-app documentation agent follows (they are one shared block; a test keeps this copy in sync).
183
+
184
+ ### Where Documentation Renders
185
+
186
+ The documentation block renders on the entity's show page and in the compiled O&M closeout package PDF — a formal handover manual the owner's facilities team keeps for decades. The manual already prints the following from structured data, so documentation never restates it:
187
+
188
+ - Identity: tag, serial number, status, install and commissioning dates, quantity, location path, manufacturer, model, spec section, classification, data-package tier.
189
+ - Warranties: kind, start and expiration dates, duration, claims contact and the terms text.
190
+ - Maintenance schedule: every plan, procedure, frequency, craft and estimated hours, built from the maintenance plan records.
191
+ - Spare parts: name, SKU, manufacturer part number, quantity and vendor, from the part records.
192
+ - Contacts: the responsible company with phone and email, plus a per-system contacts rollup.
193
+ - Linked documents: every datasheet, manual, warranty and report, listed and bound directly behind the entity's page.
194
+
195
+ ### What to Write
196
+
197
+ Spend the narrative on what tables cannot say: sequences of operation with real setpoints, startup and shutdown, seasonal and emergency procedures, troubleshooting, safety warnings, cross-system interfaces, and callouts where sources conflict. Describe the equipment, not the records — state how it works and how it is cared for, and fold genuine unknowns into one short "Open Items" list, one line each, without citations. A short block with two real sections beats a full template of filler: omit every section the sources don't genuinely cover, and leaving the block blank is a valid outcome for an entity with nothing to add.
198
+
199
+ Write for the building operator in plain language. Paraphrase manual procedures as proper markdown lists, and fix concatenation artifacts (double periods, misspelled manufacturer names) from sources.
200
+
201
+ Keep the block free of what the manual already prints and of anything that goes stale:
202
+
203
+ - No warranty terms or dates in prose, no frequency lists or task inventories, no tag/serial/model/quantity dossiers, no contractor names or phone numbers, and no parts tables pasted from manuals — replacement-parts data belongs in part records.
204
+ - No claims about the state of the records ("no dates are populated", "this task is recorded on the checklist") — they end up contradicting the printed tables.
205
+ - No restatement of the entity's `description` field as the opening — both print in sequence.
206
+ - No internal vocabulary ("portal", "normalized", "occurrence basis", "identity guard", "MaintenanceTask", "sourceMarkdown"), entity UUIDs, or extraction and slug references.
207
+ - No all-caps sentences or blockquote dumps of manual text, and no sections whose only content is "not established in reviewed source" or "see the linked manual".
208
+
209
+ ### Citations
210
+
211
+ Citations are clickable in the app and plain text in the printed manual, so keep them sparse and short: at most one per section, placed at the end of the claim it supports, as a markdown link whose display text is a short label such as "Siemens O&M p. 712" or "Warranty Table p. 5". Use citation URLs exactly as returned by the tools. The entity's bound-documents list already anchors provenance, so there are no per-sentence citations, multi-citation parentheticals, full source filenames, paragraph numbers, spreadsheet cell references, or dedicated "Source:" lines (print strips those).
212
+
213
+ ### Headings
214
+
215
+ The entity name renders as the page title, the block is embedded under a "Documentation" heading on the show page, and it may be re-rendered inside a parent entity's hierarchical view, so heading levels must compose:
216
+
217
+ - Top-level sections use `##`; sub-sections use `###`, then `####`, without skipping levels.
218
+ - Begin with the first content section (for example `## Description`). The entity's own name is never a heading and `#` is never used — that level is the page title, and a leading H1 matching the entity name is suppressed in print.
219
+ - Keep section names consistent with the templates below so sibling entities render with the same structure.
220
+
221
+ ### Section Templates
222
+
223
+ Use these section orderings with the operator-facing names shown; most entities warrant only one to three of them.
224
+
225
+ **Product** — the canonical home for manufacturer operating knowledge. A product with any linked submittal, datasheet, manual or warranty document gets a block; only a product with no linked source documents is left blank. Location and system blocks may roll product knowledge up but never replace it.
226
+
227
+ ```markdown
228
+ ## Description (1-3 sentences: what it is and its role — not metadata)
229
+ ## Operation (sequence of operation, setpoints, modes, interlocks)
230
+ ## Routine Maintenance (procedure steps and precautions — never frequencies; the schedule table prints those)
231
+ ## Troubleshooting
232
+ ## Safety
233
+ ## Open Items
234
+ ```
235
+
236
+ **System** — the system in its own words, spanning multiple products. A parent system covers only what is true of the whole (shared operation, shared safety): the manual prints the subsystems list and rolls up schedules, warranties and contacts, so content common to sibling systems lives once on the parent and each child gets at most one sentence of scope.
237
+
238
+ ```markdown
239
+ ## Description (what the system is and its boundary, 2-4 sentences)
240
+ ## Sequence of Operation
241
+ ## Interfaces (only real cross-system relationships: what feeds, releases, or controls what)
242
+ ## Seasonal & Emergency Procedures
243
+ ## Troubleshooting
244
+ ## Safety
245
+ ## Open Items
246
+ ```
247
+
248
+ **Location** — building or area overview.
249
+
250
+ ```markdown
251
+ ## Description
252
+ ## Access & Safety
253
+ ## Open Items
254
+ ```
255
+
256
+ **Asset** — installation-specific facts only; most assets need very little.
257
+
258
+ ```markdown
259
+ ## As-Installed Configuration (field settings, orientations, connections not captured by structured fields)
260
+ ## Commissioning Notes
261
+ ## Open Items
262
+ ```
263
+
264
+ **Company** — only what the contacts block cannot carry.
265
+
266
+ ```markdown
267
+ ## Escalation
268
+ ## Supplier Portal
269
+ ```
270
+
271
+ ### Example (Product)
272
+
273
+ ```markdown
274
+ ## Description
275
+ Passive rubber check valves preventing backflow from rivers, oceans, and storm water into outfall lines.
276
+
277
+ ## Operation
278
+ Flow opens the bill section; back-pressure closes it. No power or operator action required. Do not exceed 25 psi back-pressure [Manual p. 3](sb://doc/...).
279
+
280
+ ## Routine Maintenance
281
+ 1. Clear debris from the bill opening.
282
+ 2. Inspect the elastomer for cuts or abrasion; replace on any through-cut.
283
+
284
+ ## Troubleshooting
285
+ Standing water upstream usually means debris is holding the bill open — clear before assuming valve failure.
286
+ ```
287
+
288
+ ## Parts and Meter Readings
289
+
290
+ `createPart(input: { projectId, name, sku, manufacturerPartNumber, unit, quantity, minQuantity, maxQuantity, unitCost, stocked, bin, vendorId, assetLinks: [{ assetId, quantity }] })` creates a stocked part with its per-asset quantities; `createPartMovement(input: { partId, quantity, note })` records receipts (positive) and consumption (negative), and `countedQuantity` records a cycle count. Read with `paginatedParts(projectId, filters)`.
291
+
292
+ `logMeterReading(assetId, unit: Hours|Cycles, value, readAt)` records a runtime or cycle reading; meter-triggered plan tiers (`meterUnit`/`meterThreshold`) generate work from these. `meterReadings(assetId, unit, limit)` lists them.
293
+
294
+ ## Closeout Packages (O&M manual export)
295
+
296
+ A closeout package compiles the graph into one bookmarked PDF: front matter (cover, directory, warranty log, hyperlinked TOC), a slip sheet and overview per system (or location), a data page per asset, and every linked PDF document bound behind its owner (each unique file once). Scope is `Project` (everything), `Systems`, `Assets`, or `Locations` with `targetIds`; `orderingAxis` is `System` (default) or `Location`.
297
+
298
+ Check readiness first — it is deterministic and the same builder the compile uses, so a clean report means a clean manual:
299
+
300
+ ```bash
301
+ specset api --query '{ closeoutPackageReadiness(projectId: "<project-id>", scopeKind: Project) { warningCount warnings } }'
302
+ ```
303
+
304
+ Warning kinds and how to clear them: `DocumentationMissing`/`DocumentationOutdated` (write or mark documentation complete on the named Asset/Product/System/Location), `AssetFieldMissing` (serial number or install date), `WarrantyMissing` (every asset needs at least one Warranty), `ProductManualMissing` (each Product needs an `OperationManual` or `MaintenanceManual` document), `PlanWithoutProcedure` (a plan with no tiers), `AttachmentNotMergeable` (non-PDF), `TargetDeleted`. `EmptyScope` blocks creation.
305
+
306
+ ```bash
307
+ specset api --query 'mutation { createCloseoutPackageExport(input: { projectId: "<project-id>", scopeKind: Project, orderingAxis: System, includeAttachments: true, title: "… O&M Manual" }) { id workflow { status } } }'
308
+ specset api --query '{ closeoutPackageExport(id: "<export-id>") { completedAt warnings workflow { status progress error } fileResource { id name } } }'
309
+ specset files download <file-resource-id> --out manual.pdf # once workflow.status is Completed
310
+ ```
311
+
312
+ `paginatedCloseoutPackageExports(projectId)` lists prior exports; exports are immutable records — regenerate by creating a new one; `deleteCloseoutPackageExport(id)` removes one and its file. In the app the same flow lives under the project's Closeout → Packages page.
313
+
314
+ ## Enabling closeout
168
315
 
169
- Asset, Location, Product, Company, and System also have markdown documentation blocks. `update<Entity>Documentation(id, markdown)` replaces the block; `mark<Entity>DocumentationComplete(id)` attests that it is current.
316
+ `CloseoutAuthService.isCloseoutActive` requires the org's closeout SKU **and** (unless the org is in `fmMode`) the project's closeout toggle. Specset staff turn both on; when closeout operations report closeout as inactive, ask your Specset contact to enable it.
170
317
 
171
318
  ## Dedup / Re-pointing
172
319
 
@@ -0,0 +1,27 @@
1
+ ---
2
+ name: specset-issues
3
+ description: Use when working with a Specset project's design-review issues from the CLI — listing the queue by severity, tier, or verification state, and reading each issue's verdict.
4
+ allowed-tools: Bash Read AskUserQuestion
5
+ ---
6
+
7
+ # Specset Issues
8
+
9
+ Design reviews (Design Review, Drawing Review, Design Quality Review, Project Review) and issue-finding skills file **issues** on a project. Each issue can be **verified** by an adversarial agent that re-reads the cited sheets and sections and records a verdict — `Confirmed` or `Rejected` — with a short justification. New review issues are verified automatically; skill-run issues, issues filed before verification existed, and issues whose runs ended without a verdict are verified on demand.
10
+
11
+ ## Reading the queue
12
+
13
+ ```bash
14
+ specset issues list --project <projectId> # active, non-Cleanup issues
15
+ specset issues list --project <projectId> --severity High,Medium --notVerified --limit 500
16
+ specset issues list --project <projectId> --thread <reviewThreadId> --json # one review's findings
17
+ specset issues list --project <projectId> --thread <runThreadId> --org <slug> # a skill run, by the id agent chat printed
18
+ ```
19
+
20
+ - `--severity` takes a comma list (`High`, `Medium`, `Low`); `--notVerified` keeps only issues with no verdict; `--archived` switches to the archived set.
21
+ - `--thread` matches the thread and every review thread under it: a skill's issues belong to its delegated review, so the run id `specset agent chat` prints finds them directly. `--org` pins the organization like the other commands.
22
+ - Cleanup-tier issues (real but self-resolving, kept out of the review queue) are excluded unless you pass `--includeCleanup` — the same rule the app's default queue uses.
23
+ - The table shows `#number severity tier verdict title`; `verdict` is `Confirmed`, `Rejected`, `verifying` (run in flight) or `unverified`. `--json` returns the full rows including the originating review thread.
24
+
25
+ ## Verification
26
+
27
+ Verdicts come from the app's own verification runs: new review issues are verified automatically, and a member can start verification for an individual issue from the issue in the app. `specset issues list` shows the verdict on every row (`--notVerified` to find the ones still waiting).
@@ -8,6 +8,10 @@ allowed-tools: Bash Read AskUserQuestion
8
8
 
9
9
  This skill mutates data — confirm the project name and which files belong to it before creating anything. Inline enums, ints, lists, and input objects in the operation text (`-F` is strings-only; Variable Limitations and Schema Discovery live in the `specset` skill).
10
10
 
11
+ ## Find a project
12
+
13
+ Use `specset projects list --json` to list the active organization's projects with IDs and links. `--org <slug|id>` pins another organization, `--limit 20` bounds output, and `--archived` lists archived projects. Pass a returned ID or link to other project commands.
14
+
11
15
  ## Reading Projects
12
16
 
13
17
  ```graphql
@@ -20,17 +24,17 @@ project(id: ID!): Project!
20
24
 
21
25
  ## Creating a Project
22
26
 
23
- `createProject` does **not** add the creator as a project member, so the new project won't appear in the default `projects` list — fetch it by `project(id)`, and add membership as a separate step, mirroring the UI.
27
+ `createProject` does **not** add the creator as a project member, so the new project won't appear in the default `projects` list until someone adds them — fetch it by `project(id)`. The `createProject` mutation takes `userIds` and `teamIds` to add members in the same call; `specset projects create` doesn't, so add them as a second step.
24
28
 
25
- 1. Create it:
29
+ 1. Create it with the project command (new projects support drawing graphs automatically):
26
30
 
27
31
  ```bash
28
- specset api --query 'mutation($orgId: ID!, $name: String!) {
29
- createProject(orgId: $orgId, name: $name) { id name }
30
- }' -F orgId=<org-id> -F name='Riverside Medical Center'
32
+ specset projects create 'Riverside Medical Center' --json
31
33
  ```
32
34
 
33
- 2. Get your own user id with `{ me { id } }`, then add yourself (and anyone else the user names) as a member. `role` is an optional `OrgMemberTeamRole` enum — `Member`, `Manager`, or `Admin` — inlined when specified:
35
+ Select the active organization first with `specset org use <slug>`. Pass `--host <url>` to both commands when targeting a specific deployment. JSON output includes `id`, `name`, and `drawingGraphEnabled`. Existing projects qualify when every current sheet supports graph resolution (node version 5 or later); ledger failures do not disable graphs.
36
+
37
+ 2. Get your own user id with `{ me { id } }`, then add yourself (and anyone else the user names) as a member. `role` is an optional `OrgMemberTeamRole` enum — `Member`, `Manager`, or `Admin` — inlined when specified. To add several people or whole teams at once, use `addProjectMembers(projectId: ID!, userIds: [ID!], teamIds: [ID!])` (inline the lists); a team puts everyone on it on the project, and `teams(orgId)` lists them:
34
38
 
35
39
  ```bash
36
40
  specset api --query 'mutation($projectId: ID!, $userId: ID!) {
@@ -38,27 +42,31 @@ specset api --query 'mutation($projectId: ID!, $userId: ID!) {
38
42
  }' -F projectId=<project-id> -F userId=<user-id>
39
43
  ```
40
44
 
45
+ ## Review Settings
46
+
47
+ Drawing graphs rebuild automatically as project inputs change. Automatic
48
+ verification of newly filed review issues is a platform setting managed by
49
+ Specset; it does not verify the existing queue. Project Review has no separate
50
+ project toggle; its availability in the app comes from the organization's
51
+ configured agents.
52
+
41
53
  ## Importing Documents
42
54
 
43
- 1. Upload each PDF following Uploading Files in the `specset` skill. Each `completeUpload` returns a **cloud file id** — collect them.
44
- 2. Register the uploads on the project in a single call. The input object is inlined; ID variables are fine as its leaves:
55
+ For an existing project, inspect drawing review readiness with `specset projects drawing-graph --project <id> --json`. Add `--rebuild` to queue resolution from already extracted drawing content; this does not start sheet extraction or element backfill. Re-run the command without mutation flags to inspect readiness after the background rebuild. Eligibility is automatic for new projects and existing projects whose current sheets are all at node version 5 or later. Pin `--host` and `--org` when targeting a particular deployment or organization.
56
+
57
+ Use the document-import command for local files; it uploads them, registers them on the project, and publishes the generated drawing/specification files automatically:
45
58
 
46
59
  ```bash
47
- specset api --query 'mutation($projectId: ID!, $f1: ID!, $f2: ID!) {
48
- createProjectDocuments(input: {
49
- projectId: $projectId
50
- cloudFileIds: [$f1, $f2]
51
- autoImport: true
52
- }) { id totalCount completedCount failedCount }
53
- }' -F projectId=<project-id> -F f1=<cloud-file-id> -F f2=<cloud-file-id>
60
+ specset projects add-docs --project <project-id> ./drawings.pdf ./specifications.pdf --json
61
+ specset projects ingest-status --project <project-id> --wait --timeout 1800 --json
54
62
  ```
55
63
 
56
- 3. It returns a **BulkAction** — poll it per Waiting on Background Processing in the `specset` skill. Processing extracts each document's outline and content, indexes it for search, and splits it into candidate spec files and drawing files by page range.
64
+ `add-docs` returns upload metadata and the background bulk action immediately. `ingest-status --wait` follows processing and publication; omit `--wait` for a snapshot. Processing extracts each document's outline and content, indexes it for search, and splits it into candidate spec files and drawing files by page range. The status command checks workflow completion and file publication; use drawing-element or graph tools to inspect content quality.
57
65
 
58
66
  ### `autoImport`: publish automatically or curate
59
67
 
60
68
  - `autoImport: true` — every generated spec/drawing file is classified and published into default sets automatically. Use when the user wants everything live immediately.
61
- - Omit it (or `false`) — files land **unpromoted** for manual curation below. Use when the user wants to control set titles, membership, or ordering.
69
+ - Pass `--noAutoImport` to `add-docs` — files land **unpromoted** for manual curation below. Use when the user wants to control set titles, membership, or ordering. The raw `createProjectDocuments` API defaults to unpromoted when `autoImport` is omitted.
62
70
 
63
71
  Ask the user when their intent is ambiguous — this choice changes what gets published. The input also supports folders: `parentFolderId` / `newFolderName` for flat imports, or `folderTree` + `files` to mirror a directory structure — introspect `CreateProjectDocumentsInput` for those shapes.
64
72
 
@@ -98,6 +106,21 @@ updateDrawingSetPrecedence(projectId: ID!, drawingSetIds: [ID!]!): [DrawingSet!]
98
106
 
99
107
  4. Verify and summarize — query `project(id)` (not the membership-filtered `projects` list) with `specSets { id title }` and `drawingSets { id title }`, and report what was created.
100
108
 
109
+ ## Downloading Project Material
110
+
111
+ `specset projects hydrate` pulls a project's source material into a local directory so an agent (or a person) can read the PDFs directly instead of fetching sections one at a time:
112
+
113
+ ```bash
114
+ specset projects hydrate <projectId|link> --out ./pier-romeo # specs/, drawings/, docs/
115
+ specset projects hydrate <projectId|link> --out ./pier-romeo --include specs,drawings
116
+ specset projects hydrate <projectId|link> --json # default --out: ~/Specset/<host>/<project-name>-<projectId>/
117
+ ```
118
+
119
+ - `specs/` holds one PDF per spec section (`<number> - <title>.pdf`), `drawings/` one PDF per drawing sheet (`<sheet> - <title>.pdf`, the rendered image for legacy sheets), and `docs/` the project's other documents (reports, permits, manuals). The spec book and drawing set PDFs themselves are not repeated under `docs/` — fetch a whole book with `specset specs download <ref> --asset book` when needed.
120
+ - Re-running is idempotent: files already present at the stored size are skipped, so run it again after new documents are processed to pick up only what changed.
121
+ - `--json` returns `{ projectId, out, files: [{ path, kind, ref, bytes, sha256 }], skipped, failures }`; `ref` is the public link of the source entity. **Cite that link, never the local path**, when reporting findings from a downloaded file.
122
+ - Large projects can be hundreds of files; start with `--include specs` when only the specifications matter. A non-zero exit means some downloads failed — re-run the same command to retry just those.
123
+
101
124
  ## Archiving and Deleting
102
125
 
103
126
  - **Prefer `archiveProject(id: ID!)`** — reversible via `unarchiveProject(id: ID!)`. Archived projects drop out of default lists but keep all data.
@@ -108,3 +131,5 @@ updateDrawingSetPrecedence(projectId: ID!, drawingSetIds: [ID!]!): [DrawingSet!]
108
131
  - Authorization error on a mutation — the user lacks that permission; the same rules as the UI apply. Surface the message, don't retry.
109
132
  - A freshly created project "missing" from `projects` — that's the membership gotcha above, not a failure.
110
133
  - A document reports a failed processing status on the BulkAction — report it to the user; re-uploading is their call.
134
+
135
+ Hydration records each file's Specset link, version id and checksum in `.specset/manifest.json`. Inside a local run's workspace (`specset agent serve` with **Native tools** on) the single `… download` commands record there too, and a version already present is not fetched again.
@@ -9,11 +9,18 @@ allowed-tools: Bash Read AskUserQuestion
9
9
  How the data is shaped:
10
10
 
11
11
  - `status` (SubmittalStatus: `Draft | Open | Closed`) is the log lifecycle; `workingStatus` (SubmittalWorkingStatus: `Draft | Submitted | Pending | Approved | ApprovedAsNoted | Rejected | ReviseAndResubmit | ForRecordOnly | Void`) is the ball-in-court state. Set either — the server reconciles the other.
12
- - Revisions are separate submittal records sharing a `submittalNumber`; list and search queries return only the current revision.
12
+ - Revisions are separate submittal records linked through `parentSubmittal` (the chain, not a shared number, defines a revision); list and search queries return only the current revision.
13
13
  - A submittal number is only unique per (spec section, number) within a project — the bare number alone is not.
14
14
 
15
15
  The sketches below are minimal — inline enums, ints, and lists in the operation text (`-F` is strings-only; Variable Limitations and Schema Discovery live in the `specset` skill).
16
16
 
17
+ Two common flows have purpose-built commands; prefer them to hand-written mutations:
18
+
19
+ ```bash
20
+ specset submittals create --project <projectId> --file ./package.pdf --json # uploads the file and creates the submittal from it; --title, repeatable --specSection <id>
21
+ specset submittals review --submittal <submittalId|link> --wait --json # runs a compliance review and prints its issues; repeatable --specSection, --prompt, --timeout (default 3600)
22
+ ```
23
+
17
24
  ## Reading
18
25
 
19
26
  ```bash
@@ -39,7 +46,7 @@ Also available:
39
46
 
40
47
  - `paginatedSubmittals(projectId, search, limit: 25, offset: 0) { totalFilteredCount items { ... } }` — offset pagination with text search.
41
48
  - `searchSubmittals(projectId, search)` — lightweight lookup by number or title; use it for dedupe checks.
42
- - `submittalRevisions(projectId, submittalNumber, specSectionId)` — every revision of one number.
49
+ - `submittalRevisions(id)` — every revision in the given submittal's chain (itself included), highest revision first.
43
50
  - `submittalTypes(projectId) { id name label }` — the project's configured types, for `submittalTypeId`.
44
51
 
45
52
  ## Creating
@@ -66,6 +73,8 @@ specset api --query 'mutation($id: ID!) {
66
73
 
67
74
  The revision string auto-increments (`0` → `1`, `A` → `B`); pass `revision` only when the document shows one that doesn't match. `autoReviewMode` works here too.
68
75
 
76
+ A submittal created from a PDF that identifies an existing current submittal (same section and number, higher revision or a package that presents itself as a resubmittal) is linked as its next revision automatically. `unlinkSubmittalRevision(id) { id revision isCurrent parentSubmittal { id } }` detaches a wrongly linked row: it becomes a separate submittal with the number from its own document and the previous revision becomes current again.
77
+
69
78
  ## Attachments
70
79
 
71
80
  File ids come from Uploading Files in the `specset` skill.
@@ -6,7 +6,7 @@ allowed-tools: Bash Read AskUserQuestion
6
6
 
7
7
  # Specset Agent Tools (`specset tools`, `specset mcp`)
8
8
 
9
- These are the same read-only retrieval tools Specset's own in-app agent uses — exposed so external agents can call them directly. Everything here is read-only; nothing needs user confirmation.
9
+ These are the same read-only retrieval tools Specset's own in-app agent uses — exposed so external agents can call them directly.
10
10
 
11
11
  ## When to use which surface
12
12
 
@@ -22,6 +22,7 @@ There are three tiers of Specset retrieval. Pick the lightest one that answers t
22
22
  specset tools list
23
23
  specset tools list --project <projectId|https://app.specbook.ai/go/project/...>
24
24
  specset tools list --json # full input schemas
25
+ specset tools list --org <slug> # catalog for an org other than the active one
25
26
  ```
26
27
 
27
28
  The listing includes usage guidance and marks project-scoped tools. The exact set depends on the org (closeout and schedule tools require their SKUs) and on whether a project is in scope.
@@ -55,6 +56,8 @@ Tool results expose clickable links shaped like `https://app.specbook.ai/go/spec
55
56
 
56
57
  Most tools are project-scoped. Either pass `--project` (a UUID or returned `/go/project/...` link), or include `projectId` in the tool input. Use `listProjects` to discover projects. With `--project` set, per-call `projectId` becomes optional.
57
58
 
59
+ Tools use the selected organization on the chosen host, or infer the sole membership when none is selected. Pass `--org <slug|id>` on `tools list` and `tools run` to pin a different org — required when the project belongs to an org other than the active one (the call otherwise fails with "Project … not found"), and safer in parallel sessions because the active org is shared config.
60
+
58
61
  ## Downloading source files (drawings, documents, specs, submittals, RFIs)
59
62
 
60
63
  The retrieval tools return **extracted text**, which is authoritative for schedules, notes, and spec paragraphs — but cannot answer spatial/visual questions (routing, clearances, what a plan view actually shows) and never carries the original file. Every entity has a download command, and **each accepts its returned Specset `/go` link verbatim** (citation focus parameters are ignored for downloads) or a bare UUID:
@@ -70,13 +73,48 @@ The retrieval tools return **extracted text**, which is authoritative for schedu
70
73
 
71
74
  All commands support `--json` (structured `{files: [{path, bytes, ...}]}` output) and `--out` (file path, or directory for multi-file downloads). A citation passed to the wrong command errors with the right one to use.
72
75
 
76
+ `drawings download`, `files download` and `specs download` also accept `--org <slug|id>` to pin the source organization when parallel sessions might change the active organization, for example `specset drawings download P2.120 --project <id> --org <slug> --host <host> --format pdf` or `specset specs download '08 71 00' --project <id> --org <slug> --host <host> --out hardware.pdf`.
77
+
78
+ To inspect an existing detail crop, add `--node <shortId|UUID>`, for example `specset drawings download <sheet-ref> --node d6 --format image --out detail.png`. Use `--format pdf` for the saved crop PDF. This downloads the current revision's stored crop without regenerating it; missing nodes or crops return an error. Node crops do not support `--format file`. JSON output adds the resolved `nodeId` to the normal sheet download metadata.
79
+
80
+ Its JSON result includes `sheetId` and `revisionId` for the downloaded source. Compare the revision with any previously retrieved content before using that content's coordinates to crop the download.
81
+
73
82
  Typical loop for drawings: search or `getDrawingSheetContent` to find the sheet → hit a visual question the text can't settle → `drawings download` → read the PNG to inspect the geometry.
74
83
 
84
+ ### Drawn elements: what a drawing view prints, with positions
85
+
86
+ Before downloading a plan to read a tag or a value off it, check the sheet's **drawn elements**: every printed string on each Drawing node with a 0–1000 box on the node's crop (the ledger), and the tags, runs, connections, cross-references, and printed limits typed from those strings. Each typed element cites ledger rows (`d1a12` = row 12 of node `d1`), so the printed text and its box are always the row's.
87
+
88
+ ```bash
89
+ specset drawings elements show 'https://app.specbook.ai/go/drawing/<uuid>' # per node: read/typing status, counts, tags, runs, connections, references, limits, keynotes
90
+ specset drawings elements show E6.00 --project <id> --node d1 --grep 800AT # rows and elements whose text contains 800AT (implies --rows)
91
+ specset drawings elements show E6.00 --project <id> --rows --json # every row with kind, box, grid cell, rotation, cloud/revision flags
92
+ ```
93
+
94
+ Read the status line first. A successful ledger read is evidence of printed text; absence claims also need the applicable completeness, recall, revision-cloud and source-coverage gates. Partial reads or unchecked coverage need visual confirmation. A version match alone does not mean a failed extraction succeeded.
95
+
96
+ Graph tools are automatically available with `--project` for eligible projects (every current drawing sheet uses extraction version 5 or newer; empty projects also qualify). Graphs refresh automatically. To request an immediate rebuild from existing extracted content:
97
+
98
+ ```bash
99
+ specset drawings build-graph --project <id> --wait --timeout 1800 --host <host> --json
100
+ ```
101
+
102
+ Omit `--wait` to return after requesting the build. Waiting confirms a newly persisted graph snapshot, not complete ingestion or review readiness; inspect the returned statistics and `getDrawingGraphOverview` for stale nodes and bundles before reviewing. The server build continues after a timeout, so inspect the overview before requesting another build. This does not reprocess drawings.
103
+
104
+ Use `getDrawingGraphOverview` to discover bundle keys, `getCoordinationBundle` for their views, `traverseDrawingReferences` for callout links, and `findDrawingGraphElements` for labeled, schedule-derived assets and rooms. For graphical devices, including unlabeled glyphs, use the candidate symbol lookup when ingestion and a graph rebuild have populated it:
105
+
106
+ ```bash
107
+ specset tools run findDrawingGraphSymbols --project <id> --host <host> \
108
+ -F canonicalTypes='["LightFixture","Sprinkler"]' -F sheetNumber=A5.01 -F limit=20
109
+ ```
110
+
111
+ It returns source-linked boxes and their supporting legend definitions. You can also filter by `drawingArea`, `nodeId`, or a `scopeKey` returned by `getDrawingGraphOverview`; follow `nextOffset` when `hasMore` is true. These are incomplete candidate observations: zero results do not establish absence, matching types do not link devices across views, and returned counts are not verified physical totals. Confirm an issue-bearing interpretation by downloading and inspecting the source drawing with `specset drawings download <returned-link>`. The in-app visual analysis tool `examineDrawingSheets` is not exposed to external callers.
112
+
75
113
  ## MCP servers (local and hosted)
76
114
 
77
115
  The same server-side tool registry is available through two transports:
78
116
 
79
- - `specset mcp` is the local stdio transport for Claude Code, Codex, and other clients with shell access. It reuses the CLI login and active organization.
117
+ - `specset mcp` is the local stdio transport for Claude Code, Codex, and other clients with shell access. It reuses the CLI login. Pass `--org <slug|id>` to pin the organization, or use the selected org / sole membership on the chosen host.
80
118
  - `https://<specset-host>/mcp` is the stateless Streamable HTTP transport for hosted MCP clients. Its OAuth 2.1 consent flow selects an organization and binds it into the grant, so hosted clients must not put `orgId` in the connection URL. Add `projectId=<project-id>` to pin project scope.
81
119
  - First-party clients may instead supply a Specset session bearer token and select the organization with `x-org-id` (or the legacy `orgId` query parameter).
82
120
 
@@ -85,8 +123,8 @@ Register the local transport once and the client can call the tools directly:
85
123
  ```bash
86
124
  # Claude Code
87
125
  claude mcp add specset -- specset mcp
88
- # Optionally pin a project and/or host:
89
- claude mcp add specset -- specset mcp --project <projectId> --host https://app.specbook.ai
126
+ # Optionally pin organization, project and host:
127
+ claude mcp add specset -- specset mcp --org <slug> --project <projectId> --host https://app.specbook.ai
90
128
 
91
129
  # Codex
92
130
  codex mcp add specset -- specset mcp
@@ -100,6 +138,6 @@ Notes:
100
138
  - The hosted transport additionally exposes `uploadFile` (OpenAI host file parameters, streamed into Specset storage) and `getFileDownload` (an MCP resource link backed by a short-lived signed URL). The underlying retrieval tools are still the same allowlisted registry used by stdio.
101
139
  - The hosted transport advertises standards-compliant MCP OAuth 2.1 protected-resource and authorization-server metadata. It supports Client ID Metadata Documents, PKCE, resource/audience binding, scoped tools, short-lived access tokens, and rotating refresh tokens.
102
140
 
103
- ## Stability
141
+ ## Scripting
104
142
 
105
- This surface is experimental — tool names and input schemas may change. For automation you need to keep stable, prefer the typed `search` / `quickSearch` GraphQL queries in the `specset-search` skill.
143
+ Tools are discovered at runtime (`specset tools list`, MCP `tools/list`), and their descriptions and inputs are tuned for agents. For scripts that depend on a fixed response shape, use the typed `search` / `quickSearch` GraphQL queries in the `specset-search` skill.