@reunionstudio/airlock-mcp 0.1.2 → 0.1.3

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.
@@ -1,13 +1,16 @@
1
1
  ---
2
2
  name: airlock-mcp
3
- description: Design, draft, review, and iterate Airlock specs in the Airlock MCP spec-building workbench. Use when Codex is helping a person choose a small starting spec, work through row grain and OODA-loop decisions, import reusable patterns such as posts or guest access setups, validate local draft files, or prepare an Airlock spec config before installed Airlock validation.
3
+ description: Design, draft, review, and iterate Airlock specs in the Airlock MCP spec-building workbench, or help build apps and workflows that use existing Airlock specs safely. Use when Codex is helping a person choose a small starting spec, work through row grain and OODA-loop decisions, import reusable patterns such as posts or guest access setups, validate local draft files, prepare an Airlock spec config before installed Airlock validation, or code an app that reads from and submits through existing specs.
4
4
  ---
5
5
 
6
- # Airlock MCP Spec Builder
6
+ # Airlock MCP
7
7
 
8
8
  Use this skill to help a human develop Airlock specs over multiple sessions.
9
- The repo is the workbench; Codex is the conversational interface; the CLI is
10
- the deterministic checker.
9
+ Also use it when a human wants to build an app or workflow that uses existing
10
+ Airlock specs. The specs repo is the durable workbench for spec drafts; an app
11
+ repo is the right place for code that reads, orients, decides, and submits
12
+ through existing specs. Codex is the conversational interface; the CLI is the
13
+ deterministic checker for local spec drafts.
11
14
 
12
15
  ## Default Path
13
16
 
@@ -16,6 +19,13 @@ the deterministic checker.
16
19
  `sample.records.json`, and `review.md`.
17
20
  2. If the current repo is not yet an Airlock MCP specs repo, run or suggest:
18
21
  `airlock-mcp init-repo`.
22
+ Exception: if the user asks to build an app, dashboard, approval queue,
23
+ decision UI, agent workflow, or other code that uses existing specs, do not
24
+ bootstrap a specs repo just because the current repo lacks `workspaces/`.
25
+ Treat the current repo as an app repo unless the user asks to create specs.
26
+ Ask for the development mode, available specs, app goal, and read/write
27
+ contract instead. Use `airlock-mcp init-app-context` when the app repo needs
28
+ an `airlock/` folder with spec snapshots and an app manifest.
19
29
  When helping create that repo from scratch, ask for the project or
20
30
  organization name and suggest `<slug>-specs`; for example, `Home` becomes
21
31
  `home-specs`. Reserve `airlock-specs` for the canonical reusable spec
@@ -35,7 +45,9 @@ the deterministic checker.
35
45
  any workspace.
36
46
  If the user opened the public `airlock-mcp` workbench repo as the entry
37
47
  point, help them create a separate `<slug>-specs` project repo beside it.
38
- 3. If no workspace exists, welcome the user and ask:
48
+ 3. If no workspace exists, welcome the user and ask which mode they want:
49
+ spec-first, app-first from existing specs, or co-development.
50
+ For spec-first or co-development, ask:
39
51
  `What process do you want to improve?`
40
52
  Explain that Airlock works best when we can identify the loop around that
41
53
  process:
@@ -159,6 +171,84 @@ Use the observe-orient-decide-act loop as the product frame:
159
171
  - Act: controlled writes back to interfaces, commitments, follow-ups, outputs,
160
172
  and the next observations created by those actions.
161
173
 
174
+ ## Apps And Workflows Using Existing Specs
175
+
176
+ Use this path when the user wants to vibe-code an app, dashboard, queue,
177
+ decision UI, analysis workflow, or agent workflow that uses specs they already
178
+ have access to. This is not spec editing by default.
179
+
180
+ Use three delivery modes:
181
+
182
+ - Spec-first: design governed specs, samples, access, and validation before
183
+ building the app surface.
184
+ - App-first: use existing specs to build an app, dashboard, queue, or workflow.
185
+ - Co-development: develop the app and specs together, keeping the contract and
186
+ experience visible side by side.
187
+
188
+ Start by identifying:
189
+
190
+ 1. The app goal: what the user needs to orient around or decide.
191
+ 2. Read specs: existing specs the app reads from, such as budgets, expenses,
192
+ requests, forecasts, observations, payouts, or reference data.
193
+ 3. Write specs: existing specs the app submits to, such as decisions,
194
+ approvals, comments, commitments, actions, or follow-ups.
195
+ 4. The app surface: Streamlit, web app, CLI, notebook, scheduled agent, or
196
+ other runtime.
197
+ 5. The Airlock access path: installed Airlock procedures, approved MCP tools,
198
+ generated read surfaces, stages, or documented SQL helpers available in the
199
+ user's environment.
200
+ 6. Identity, evidence, timestamps, approval, and separation-of-duties rules.
201
+
202
+ Help the app follow the loop:
203
+
204
+ - Observe/read: fetch existing governed data through approved Airlock or
205
+ Snowflake surfaces.
206
+ - Orient: summarize, compare, rank, flag exceptions, propose options, or build
207
+ dashboards and queues.
208
+ - Decide: capture human or agent choices with rationale and evidence.
209
+ - Act/write: submit the decision, approval, action, or follow-up through an
210
+ Airlock spec contract.
211
+
212
+ Keep the boundary clear:
213
+
214
+ - Do not edit specs unless the user asks for spec changes.
215
+ - Do not write directly to Airlock-owned tables, stages, generated views, or
216
+ generated tables.
217
+ - Do not treat Snowflake as an unrestricted database just because the app can
218
+ connect to it.
219
+ - Prefer existing Airlock contracts for reads and writes. If no suitable write
220
+ spec exists, explain the gap and propose a small decision/action spec as a
221
+ separate spec-design step.
222
+ - Keep secrets and credentials out of repo files. Use the app's existing secret
223
+ management pattern.
224
+
225
+ ### App Context Seeding
226
+
227
+ Use `airlock-mcp init-app-context` in an app repo when the app needs local
228
+ Airlock context. The command creates:
229
+
230
+ ```text
231
+ airlock/
232
+ AGENTS.md
233
+ README.md
234
+ specs.manifest.json
235
+ spec-snapshots/
236
+ sample-records/
237
+ generated/
238
+ types/
239
+ sql/
240
+ ```
241
+
242
+ The manifest and snapshots help the app develop against stable local references.
243
+ They are not canonical. Canonical specs live in the specs repo or installed
244
+ Airlock. When seeding from draft workspaces, pass `--spec <workspace>` for each
245
+ spec to copy `spec.config.json` and `sample.records.json` into the app context.
246
+
247
+ In co-development mode, keep two tracks visible:
248
+
249
+ - Spec track: row grain, columns, samples, access, validation, workflow.
250
+ - App track: screens, reads, decisions, writes, user actions, runtime.
251
+
162
252
  ## Pattern Guidance
163
253
 
164
254
  Read only the relevant pattern files:
@@ -1,4 +1,4 @@
1
1
  interface:
2
2
  display_name: "Airlock MCP"
3
- short_description: "Improve a process with Airlock specs and OODA loop planning."
4
- default_prompt: "Use $airlock-mcp to ask what process I want to improve, gather any existing artifacts, identify the loop around it, draft a small first Airlock spec, and keep a plan for more specs."
3
+ short_description: "Design specs or build apps that use Airlock specs."
4
+ default_prompt: "Use $airlock-mcp to ask whether I want to design specs, improve an OODA loop, or build an app/workflow that uses existing specs; then gather artifacts or available specs and help me move safely."
package/README.md CHANGED
@@ -8,18 +8,22 @@ It covers the full Airlock loop:
8
8
  - design specs with the bundled spec-building workbench
9
9
  - map the process a person wants to improve into observe, orient, decide, and act
10
10
  - use specs for governed data movement, decisions, actions, and feedback loops
11
+ - build apps and workflows that read from and submit through existing specs
11
12
  - validate, create, and revise specs against installed Airlock
12
13
 
13
- Spec building is not a second thing users install. It is bundled inside
14
- Airlock MCP.
14
+ Spec building and spec-using app guidance are not second things users install.
15
+ They are bundled inside Airlock MCP.
15
16
 
16
- Airlock MCP gives agents two kinds of Airlock expertise:
17
+ Airlock MCP gives agents three kinds of Airlock help:
17
18
 
18
19
  1. Spec design: draft, check, revise, import, clone, and prepare specs for
19
20
  installed Airlock validation.
20
21
  2. Airlock operating patterns: use specs to organize observations, orientation,
21
22
  governed decisions, controlled actions, separation of duties, and feedback
22
23
  loops.
24
+ 3. App and workflow implementation: build dashboards, queues, decision UIs,
25
+ analyses, and agent workflows that use existing specs through Airlock
26
+ contracts.
23
27
 
24
28
  ## Install
25
29
 
@@ -50,7 +54,8 @@ codex mcp add airlock -- npx -y github:reunionstudio/airlock-mcp server
50
54
 
51
55
  The server exposes bootstrap guidance for starting a specs repo, coaching a
52
56
  person through process discovery, and entering the bundled spec-building
53
- workbench when a first spec is ready to draft.
57
+ workbench when a first spec is ready to draft. It also guides agents building
58
+ apps or workflows that use specs the user already has access to.
54
59
 
55
60
  Workspace summaries are structured spec cards. They present the current spec
56
61
  core, file rules, attachment policy, guest access, column rules, sample record
@@ -90,6 +95,8 @@ The server exposes orientation plus local spec-building tools:
90
95
  - `airlock_start`: return setup guidance for a project.
91
96
  - `airlock_doctor`: verify bundled workbench assets.
92
97
  - `airlock_init_repo`: bootstrap a Git-backed specs repo.
98
+ - `airlock_init_app_context`: seed an app repo with spec snapshots, sample
99
+ records, generated helper folders, and an app manifest.
93
100
  - `airlock_list_patterns` and `airlock_show_pattern`: inspect starter patterns.
94
101
  - `airlock_init_workspace`: create a workspace from `blank` or `posts`.
95
102
  - `airlock_list_workspaces`: inspect active or archived drafts.
@@ -127,13 +134,48 @@ The intended user flow is:
127
134
  4. If Codex is creating the repo, choose where the `home-specs` directory should
128
135
  live before files are written.
129
136
  5. Ask Codex to use Airlock MCP to help improve a process with Airlock specs.
130
- 6. Let Airlock MCP bootstrap the project, ask what process the user wants to
131
- improve, and propose a small first spec plus a plan for more.
137
+ 6. Choose a delivery mode: spec-first, app-first from existing specs, or
138
+ co-development of specs and app together.
139
+ 7. Let Airlock MCP bootstrap the project, ask what process the user wants to
140
+ improve when specs are involved, and propose a small first spec plus a plan
141
+ for more.
142
+
143
+ The first workspace should not be created automatically. Airlock MCP should
144
+ first ask whether the user wants spec-first, app-first from existing specs, or
145
+ co-development of specs and app together. For spec-building work, it should ask
146
+ for the messy process, identify where information comes in and actions go out,
147
+ then choose a small observation, orient, decision, or action spec.
148
+
149
+ For app-building work, Airlock MCP should identify the app goal, read specs,
150
+ write specs, orienting views, decision capture, and approved Airlock/Snowflake
151
+ access paths. The app should submit decisions, approvals, actions, comments, or
152
+ follow-ups through Airlock spec contracts. It should not write directly to
153
+ Airlock-owned tables or bypass spec workflow.
154
+
155
+ When an app repo needs local Airlock context, use:
132
156
 
133
- The first workspace should not be created automatically. The spec-building
134
- workbench should first ask for the messy process, identify where information
135
- comes in and actions go out, then choose a small observation, orient, decision,
136
- or action spec.
157
+ ```bash
158
+ airlock-mcp init-app-context . --mode app-first --spec ../home-specs/workspaces/expenses
159
+ ```
160
+
161
+ That creates:
162
+
163
+ ```text
164
+ airlock/
165
+ AGENTS.md
166
+ README.md
167
+ specs.manifest.json
168
+ spec-snapshots/
169
+ sample-records/
170
+ generated/
171
+ types/
172
+ sql/
173
+ ```
174
+
175
+ The snapshots are for coding, tests, and UI planning. They are not canonical.
176
+ Canonical specs live in the specs repo or installed Airlock. In
177
+ co-development, keep a visible spec track and app track so changes to row
178
+ grain, access, workflow, screens, reads, and governed writes stay aligned.
137
179
 
138
180
  When the user already has artifacts, Airlock MCP should ask for them early:
139
181
  CSV or Excel files, JSON samples, API docs, schemas, forms, screenshots, PDFs,
@@ -30,8 +30,9 @@ The spec-building workbench lives in this repo under `src/airlock_mcp`,
30
30
  `patterns`, `workspaces`, `schemas`, and `.agents/skills/airlock-mcp`.
31
31
  Airlock operating patterns live in the same MCP experience: real use cases,
32
32
  controlled interface ingestion, OODA loops, governed decisions, separation of
33
- duties, controlled actions, output review, and improvement capture. This is not
34
- a second install or separate product surface.
33
+ duties, controlled actions, output review, improvement capture, and app/workflow
34
+ implementation against existing specs. This is not a second install or separate
35
+ product surface.
35
36
 
36
37
  ## MCP Surface
37
38
 
@@ -41,14 +42,24 @@ The bootstrap server exposes:
41
42
  - resource: `airlock://getting-started`
42
43
  - orientation tool: `airlock_start`
43
44
  - workbench tools: `airlock_doctor`, `airlock_init_repo`,
44
- `airlock_list_patterns`, `airlock_show_pattern`, `airlock_init_workspace`,
45
- `airlock_list_workspaces`, `airlock_check_workspace`, `airlock_summary`,
46
- `airlock_next`, `airlock_export_csv`, and `airlock_render_sql`
45
+ `airlock_init_app_context`, `airlock_list_patterns`,
46
+ `airlock_show_pattern`, `airlock_init_workspace`, `airlock_list_workspaces`,
47
+ `airlock_check_workspace`, `airlock_summary`, `airlock_next`,
48
+ `airlock_export_csv`, and `airlock_render_sql`
47
49
 
48
50
  These give the agent enough context to start a Git-backed `<project>-specs`
49
51
  repo in a user-chosen location, ask what process the user wants to improve,
50
52
  enter the spec-building workbench when a first spec is ready to draft, and
51
- avoid creating a first workspace until the user chooses a path.
53
+ avoid creating a first workspace until the user chooses a path. They also give
54
+ the agent enough product guidance to work in an app repo when the user wants to
55
+ build software that reads from existing specs and submits governed decisions or
56
+ actions through Airlock contracts.
57
+
58
+ `airlock_init_app_context` is the bridge for app-first and co-development work.
59
+ It creates an app-local `airlock/` folder with spec snapshots, sample records,
60
+ generated helper placeholders, and `specs.manifest.json`. Those files support
61
+ coding, tests, and UI planning, but they are not canonical. Canonical specs
62
+ remain in the specs repo or installed Airlock.
52
63
 
53
64
  Workbench tools call the bundled Python package with argument arrays and a
54
65
  controlled `PYTHONPATH`. They do not expose update commands over MCP. Tool
@@ -16,8 +16,10 @@ Then the user opens Codex, creates or opens a Git-backed `<project>-specs`
16
16
  repo, and starts chatting with Airlock. GitHub is the recommended default when
17
17
  available. If Codex is creating the repo, it should ask where the directory
18
18
  should live before writing files. Airlock MCP should start by asking what
19
- process the user wants to improve, then help design specs and operating
20
- patterns around observe, orient, decide, and act.
19
+ delivery mode the user wants: spec-first, app-first from existing specs, or
20
+ co-development of specs and app together. Then it should help design specs and
21
+ operating patterns around observe, orient, decide, and act, or help code an app
22
+ that reads and submits through Airlock contracts.
21
23
 
22
24
  The install command is not the spec workspace. It is the connector/setup entry
23
25
  point. The specs repo is still the durable memory.
@@ -119,3 +121,31 @@ The first release should optimize the user journey:
119
121
  7. map where information comes in and actions go out
120
122
  8. create the first workspace only after the user chooses a small first spec
121
123
  9. keep a plan for later specs that improve the full loop
124
+
125
+ For users who already have specs and want to build software, optimize a second
126
+ journey:
127
+
128
+ 1. install connector
129
+ 2. open the app repo in Codex
130
+ 3. ask for the app goal and available specs
131
+ 4. identify read specs and write specs
132
+ 5. seed app-local Airlock context with `airlock-mcp init-app-context` when the
133
+ app needs spec snapshots, samples, generated helpers, and a manifest
134
+ 6. build orienting views, dashboards, queues, proposals, or recommendations
135
+ 7. capture decisions, approvals, actions, comments, or follow-ups through
136
+ Airlock spec contracts
137
+ 8. avoid direct writes to Airlock-owned tables and avoid bypassing spec workflow
138
+
139
+ For users who are developing the app and specs together, optimize a third
140
+ journey:
141
+
142
+ 1. open the app repo or specs repo in Codex
143
+ 2. choose co-development explicitly
144
+ 3. keep a spec track for row grain, columns, access, samples, validation, and
145
+ workflow
146
+ 4. keep an app track for screens, reads, orienting summaries, decision capture,
147
+ governed writes, runtime, and tests
148
+ 5. seed the app repo with `airlock/` snapshots and a manifest when the app needs
149
+ local contract context
150
+ 6. treat app snapshots as development references while canonical specs remain
151
+ in the specs repo or installed Airlock
@@ -14,6 +14,8 @@ Airlock MCP is intentionally small:
14
14
  - `art.py`: small terminal identity and about text.
15
15
  - `bootstrap.py`: specs-repo bootstrap files, including `AGENTS.md` and the
16
16
  repo-scoped Codex skill.
17
+ - `app_context.py`: app-repo Airlock context seeding for spec snapshots,
18
+ sample records, generated helper placeholders, and `specs.manifest.json`.
17
19
  - `manage.py`: workspace discovery, archive, restore, rename, and next-action
18
20
  helpers.
19
21
  - `project.py`: checkout discovery and environment override handling.
@@ -55,6 +57,8 @@ The old Airlock Streamlit editor used mode-specific hydration:
55
57
  Airlock MCP maps that to files:
56
58
 
57
59
  - `init-repo`: prepare a separate specs repo for Codex and Airlock MCP
60
+ - `init-app-context`: prepare an app repo `airlock/` folder for app-first or
61
+ co-development work against existing specs
58
62
  - `init`: create from a pattern
59
63
  - `import-spec`: edit/import an existing canonical or spec-library config in a
60
64
  new workspace
@@ -68,6 +72,12 @@ Airlock MCP maps that to files:
68
72
  This repo does not try to be the installed Airlock editor. It gives Codex and
69
73
  humans a careful drafting surface before Airlock receives the final config.
70
74
 
75
+ `init-app-context` does not make app-local snapshots canonical. It gives an app
76
+ repo stable development references for coding, tests, UI planning, and generated
77
+ helpers. Canonical specs remain in the specs repo or installed Airlock. Use
78
+ `--force` to refresh snapshots from canonical workspace files after the spec
79
+ changes.
80
+
71
81
  ## Update Boundary
72
82
 
73
83
  `self-update` supports two intentionally simple paths:
@@ -119,9 +129,10 @@ npx @reunionstudio/airlock-mcp install
119
129
 
120
130
  The connector implementation belongs in `reunionstudio/airlock-mcp`. Airlock
121
131
  MCP is the single installed interface for agents working with Airlock: building
122
- specs, using specs to pull and push governed data, and capturing improvements
123
- from real use cases. This workbench owns the spec-building implementation:
124
- patterns, bootstrap, workspace files, local checks, and the Codex skill.
132
+ specs, using specs to pull and push governed data, building apps or workflows
133
+ that use existing specs, and capturing improvements from real use cases. This
134
+ workbench owns the spec-building implementation: patterns, bootstrap, workspace
135
+ files, local checks, and the Codex skill.
125
136
 
126
137
  That command is the agent connector/setup entry point, not the user's spec
127
138
  workspace. After installing or registering the connector, the user still creates
package/docs/workflows.md CHANGED
@@ -87,9 +87,13 @@ The MCP-style flow should be:
87
87
  4. If Codex is creating it, choose where the repo directory should live before
88
88
  files are written.
89
89
  5. Tell Codex: `Use Airlock to help me improve a process with specs.`
90
- 6. Airlock MCP welcomes, asks what process the user wants to improve, explains
91
- the loop around the process, and proposes a small first spec plus a plan for
92
- more.
90
+ 6. Airlock MCP welcomes and asks which delivery mode the user wants:
91
+ spec-first, app-first from existing specs, or co-development.
92
+ 7. For spec-first or co-development, Airlock MCP asks what process the user
93
+ wants to improve, explains the loop around the process, and proposes a small
94
+ first spec plus a plan for more.
95
+ 8. For app-first or co-development, Airlock MCP identifies the app goal,
96
+ available specs, read specs, write specs, runtime, and approved access path.
93
97
 
94
98
  `init-repo` creates `AGENTS.md`, `.agents/skills/airlock-mcp/SKILL.md`, and
95
99
  `workspaces/`. That is what lets Codex understand prompts such as
@@ -130,6 +134,86 @@ current artifacts and record the divergence.
130
134
  Ask for the messy version. Help turn it into a small first Airlock spec and a
131
135
  plan for more.
132
136
 
137
+ ## Build An App From Existing Specs
138
+
139
+ Use this when the user already has access to specs and wants to build an app,
140
+ dashboard, approval queue, decision UI, analysis workflow, scheduled agent, or
141
+ other code that reads from and submits through Airlock.
142
+
143
+ Do not bootstrap or edit a specs workspace by default. Work in the app repo
144
+ unless the user asks to change specs. Start by asking for:
145
+
146
+ - the app goal and decision the app should support
147
+ - the specs the user can access
148
+ - read specs, such as budgets, expenses, requests, forecasts, observations,
149
+ payouts, or reference data
150
+ - write specs, such as decisions, approvals, comments, commitments, actions, or
151
+ follow-ups
152
+ - where the app should run, such as Streamlit, web app, CLI, notebook,
153
+ scheduled agent, or an existing framework
154
+ - the available Airlock/Snowflake access path
155
+ - identity, evidence, timestamp, approval, and separation-of-duties needs
156
+
157
+ The app should follow the loop:
158
+
159
+ 1. Observe/read governed records through approved Airlock or Snowflake surfaces.
160
+ 2. Orient with summaries, comparisons, ranked options, recommendations,
161
+ exception queues, dashboards, or proposals.
162
+ 3. Decide by capturing the human or agent choice with rationale and evidence.
163
+ 4. Act/write by submitting the decision, approval, action, comment, or follow-up
164
+ through an Airlock spec contract.
165
+
166
+ Keep the boundary explicit: do not write directly to Airlock-owned tables,
167
+ stages, generated views, or generated tables. Do not bypass spec workflow just
168
+ because the app can connect to Snowflake. If no suitable decision or action spec
169
+ exists, explain the gap and propose a small spec-design step.
170
+
171
+ If the app repo needs local context for coding, seed it with spec snapshots and
172
+ a manifest:
173
+
174
+ ```bash
175
+ airlock-mcp init-app-context . --mode app-first --spec ../home-specs/workspaces/expenses
176
+ ```
177
+
178
+ Use `--mode co-development` when the app and specs will evolve together. Repeat
179
+ `--spec` for each workspace or spec JSON file the app should know about. The
180
+ command creates:
181
+
182
+ ```text
183
+ airlock/
184
+ AGENTS.md
185
+ README.md
186
+ specs.manifest.json
187
+ spec-snapshots/
188
+ sample-records/
189
+ generated/
190
+ types/
191
+ sql/
192
+ ```
193
+
194
+ The app repo can import or generate helper code from these snapshots, but the
195
+ snapshots are not canonical. Canonical specs remain in the specs repo or
196
+ installed Airlock. Mark each manifest spec as read, write, or read_write for
197
+ the app before building data access and submission flows.
198
+
199
+ ## Co-Develop Specs And App
200
+
201
+ Use this when the app surface and the Airlock contracts need to shape each
202
+ other. This is common when a dashboard, queue, approval flow, or agent workflow
203
+ reveals which observations, proposals, decisions, or actions need governed
204
+ records.
205
+
206
+ Keep two tracks visible:
207
+
208
+ - Spec track: row grain, columns, samples, attachments, access, validation,
209
+ workflow, and installed Airlock validation.
210
+ - App track: screens, reads, summaries, ranked options, decision capture,
211
+ governed writes, runtime, and tests.
212
+
213
+ Start with the smallest useful contract. The app may expose a thin prototype
214
+ against samples while the spec is being drafted, but live submissions should
215
+ wait for installed Airlock validation and the approved read/write path.
216
+
133
217
  ## Start From Feedback
134
218
 
135
219
  Use this when the user chooses a shared feedback loop for humans and agents.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@reunionstudio/airlock-mcp",
3
- "version": "0.1.2",
3
+ "version": "0.1.3",
4
4
  "description": "Single-install MCP interface for Airlock agents.",
5
5
  "type": "module",
6
6
  "repository": {
package/setup.py CHANGED
@@ -27,7 +27,7 @@ def data_files():
27
27
 
28
28
  setup(
29
29
  name="airlock-mcp",
30
- version="0.1.2",
30
+ version="0.1.3",
31
31
  description="Codex-first workbench and CLI for drafting Airlock specs.",
32
32
  packages=find_packages("src"),
33
33
  package_dir={"": "src"},
@@ -1,3 +1,3 @@
1
1
  """Airlock MCP CLI package."""
2
2
 
3
- __version__ = "0.1.2"
3
+ __version__ = "0.1.3"
@@ -0,0 +1,271 @@
1
+ from __future__ import annotations
2
+
3
+ import re
4
+ from dataclasses import dataclass
5
+ from pathlib import Path
6
+ from typing import Any
7
+
8
+ from .jsonio import read_json, write_json, write_text
9
+ from .specs import extract_spec_config, spec_name
10
+
11
+
12
+ APP_CONTEXT_MODES = ("spec-first", "app-first", "co-development")
13
+
14
+
15
+ APP_README = """# Airlock App Context
16
+
17
+ This folder is app-local context for building software that uses Airlock specs.
18
+
19
+ The canonical specs live in the specs repo or installed Airlock. Files here are
20
+ snapshots, samples, generated helpers, and planning context for app development.
21
+ Refresh them when the canonical spec changes.
22
+
23
+ Use:
24
+
25
+ - `specs.manifest.json` to track which specs the app reads from or writes to.
26
+ - `spec-snapshots/` for app-local copies of spec configs.
27
+ - `sample-records/` for app-local sample records used in UI and test fixtures.
28
+ - `generated/types/` for generated application types.
29
+ - `generated/sql/` for reviewed SQL helpers or query templates.
30
+
31
+ Do not store credentials here. Do not write directly to Airlock-owned tables,
32
+ stages, generated views, or generated tables. Use approved Airlock/Snowflake
33
+ access paths and submit governed decisions or actions through spec contracts.
34
+ """
35
+
36
+
37
+ APP_AGENTS = """# Airlock App Guidance
38
+
39
+ This repo may contain application code that uses Airlock specs.
40
+
41
+ ## Modes
42
+
43
+ - Spec-first: design governed specs before building the app surface.
44
+ - App-first: use existing specs to build an app, dashboard, queue, or workflow.
45
+ - Co-development: evolve the app and specs together while keeping the contract
46
+ explicit.
47
+
48
+ ## App Context
49
+
50
+ The `airlock/` folder is app-local. Its spec snapshots are references for
51
+ development, not canonical specs. Canonical specs live in the specs repo or
52
+ installed Airlock.
53
+
54
+ Keep two tracks visible during co-development:
55
+
56
+ - Spec track: row grain, columns, samples, access, validation, and workflow.
57
+ - App track: screens, reads, decisions, writes, user actions, and runtime.
58
+
59
+ Use approved Airlock/Snowflake access paths. Do not bypass spec workflow or
60
+ write directly to Airlock-owned tables, stages, generated views, or generated
61
+ tables. If the app needs to submit a decision or action and no suitable write
62
+ spec exists, propose a small spec-design step.
63
+ """
64
+
65
+
66
+ @dataclass(frozen=True)
67
+ class AppContextResult:
68
+ root: Path
69
+ mode: str
70
+ created: tuple[Path, ...]
71
+ updated: tuple[Path, ...]
72
+ kept: tuple[Path, ...]
73
+ specs: tuple[str, ...]
74
+
75
+
76
+ def _safe_name(value: str) -> str:
77
+ cleaned = re.sub(r"[^a-zA-Z0-9_.-]+", "_", value.strip())
78
+ cleaned = cleaned.strip("._-").lower()
79
+ return cleaned or "spec"
80
+
81
+
82
+ def _write_text_if_needed(path: Path, text: str, *, force: bool) -> str:
83
+ if path.exists() and not force:
84
+ return "kept"
85
+ path.parent.mkdir(parents=True, exist_ok=True)
86
+ write_text(path, text, force=True)
87
+ return "created"
88
+
89
+
90
+ def _write_json_if_needed(path: Path, value: Any, *, force: bool) -> str:
91
+ if path.exists() and not force:
92
+ return "kept"
93
+ path.parent.mkdir(parents=True, exist_ok=True)
94
+ write_json(path, value, force=True)
95
+ return "created"
96
+
97
+
98
+ def _load_spec_source(source: Path) -> tuple[dict[str, Any], dict[str, Any] | None]:
99
+ if source.is_dir():
100
+ raw_spec = read_json(source / "spec.config.json")
101
+ if not isinstance(raw_spec, dict):
102
+ raise ValueError(f"invalid spec workspace: {source}")
103
+ raw_sample = read_json(source / "sample.records.json")
104
+ return raw_spec, raw_sample if isinstance(raw_sample, dict) else None
105
+
106
+ raw = read_json(source)
107
+ if raw is None:
108
+ raise FileNotFoundError(source)
109
+ return extract_spec_config(raw), None
110
+
111
+
112
+ def _manifest(mode: str, entries: list[dict[str, Any]]) -> dict[str, Any]:
113
+ return {
114
+ "schema_version": 1,
115
+ "mode": mode,
116
+ "canonical_source": "specs repo or installed Airlock",
117
+ "snapshot_policy": "Snapshots are app-local development references, not canonical specs.",
118
+ "specs": entries,
119
+ "tracks": {
120
+ "spec_track": "row grain, columns, samples, access, validation, workflow",
121
+ "app_track": "screens, reads, decisions, writes, user actions, runtime",
122
+ },
123
+ }
124
+
125
+
126
+ def _merge_manifest(existing: Any, new_manifest: dict[str, Any]) -> dict[str, Any]:
127
+ if not isinstance(existing, dict):
128
+ return new_manifest
129
+
130
+ merged = dict(existing)
131
+ merged.update(
132
+ {
133
+ "schema_version": new_manifest["schema_version"],
134
+ "mode": new_manifest["mode"],
135
+ "canonical_source": new_manifest["canonical_source"],
136
+ "snapshot_policy": new_manifest["snapshot_policy"],
137
+ "tracks": new_manifest["tracks"],
138
+ }
139
+ )
140
+
141
+ existing_entries = existing.get("specs")
142
+ by_name: dict[str, dict[str, Any]] = {}
143
+ if isinstance(existing_entries, list):
144
+ for entry in existing_entries:
145
+ if isinstance(entry, dict) and isinstance(entry.get("spec_name"), str):
146
+ by_name[entry["spec_name"]] = dict(entry)
147
+
148
+ for entry in new_manifest["specs"]:
149
+ previous = by_name.get(entry["spec_name"], {})
150
+ merged_entry = {**previous, **entry}
151
+ if previous.get("role") and entry.get("role") == "unknown":
152
+ merged_entry["role"] = previous["role"]
153
+ by_name[entry["spec_name"]] = merged_entry
154
+
155
+ merged["specs"] = list(by_name.values())
156
+ return merged
157
+
158
+
159
+ def init_app_context(
160
+ target: Path,
161
+ *,
162
+ mode: str = "app-first",
163
+ spec_sources: list[Path] | None = None,
164
+ force: bool = False,
165
+ ) -> AppContextResult:
166
+ if mode not in APP_CONTEXT_MODES:
167
+ raise ValueError(f"mode must be one of: {', '.join(APP_CONTEXT_MODES)}")
168
+
169
+ root = target.expanduser().resolve()
170
+ if root.exists() and not root.is_dir():
171
+ raise NotADirectoryError(str(root))
172
+ root.mkdir(parents=True, exist_ok=True)
173
+
174
+ airlock_root = root / "airlock"
175
+ created: list[Path] = []
176
+ updated: list[Path] = []
177
+ kept: list[Path] = []
178
+
179
+ for directory in (
180
+ airlock_root,
181
+ airlock_root / "spec-snapshots",
182
+ airlock_root / "sample-records",
183
+ airlock_root / "generated",
184
+ airlock_root / "generated" / "types",
185
+ airlock_root / "generated" / "sql",
186
+ ):
187
+ if directory.exists():
188
+ kept.append(directory)
189
+ else:
190
+ directory.mkdir(parents=True)
191
+ created.append(directory)
192
+
193
+ entries: list[dict[str, Any]] = []
194
+ spec_names: list[str] = []
195
+ for source in spec_sources or []:
196
+ spec_config, sample_records = _load_spec_source(source.expanduser())
197
+ name = spec_name(spec_config)
198
+ safe = _safe_name(name)
199
+ snapshot_path = airlock_root / "spec-snapshots" / f"{safe}.spec.config.json"
200
+ bucket = created if _write_json_if_needed(snapshot_path, spec_config, force=force) == "created" else kept
201
+ bucket.append(snapshot_path)
202
+
203
+ sample_relative: str | None = None
204
+ if sample_records is not None:
205
+ sample_path = airlock_root / "sample-records" / f"{safe}.sample.records.json"
206
+ bucket = created if _write_json_if_needed(sample_path, sample_records, force=force) == "created" else kept
207
+ bucket.append(sample_path)
208
+ sample_relative = str(sample_path.relative_to(root))
209
+
210
+ core = spec_config.get("core_config")
211
+ core = core if isinstance(core, dict) else {}
212
+ entries.append(
213
+ {
214
+ "spec_name": name,
215
+ "spec_alias": core.get("spec_alias"),
216
+ "source": str(source),
217
+ "snapshot": str(snapshot_path.relative_to(root)),
218
+ "sample_records": sample_relative,
219
+ "role": "unknown",
220
+ "snapshot_only": True,
221
+ }
222
+ )
223
+ spec_names.append(name)
224
+
225
+ for path, content in (
226
+ (airlock_root / "README.md", APP_README),
227
+ (airlock_root / "AGENTS.md", APP_AGENTS),
228
+ (airlock_root / "generated" / "types" / ".gitkeep", ""),
229
+ (airlock_root / "generated" / "sql" / ".gitkeep", ""),
230
+ ):
231
+ bucket = created if _write_text_if_needed(path, content, force=force) == "created" else kept
232
+ bucket.append(path)
233
+
234
+ manifest_path = airlock_root / "specs.manifest.json"
235
+ manifest = _manifest(mode, entries)
236
+ if manifest_path.exists() and not force:
237
+ write_json(manifest_path, _merge_manifest(read_json(manifest_path), manifest), force=True)
238
+ updated.append(manifest_path)
239
+ else:
240
+ bucket = created if _write_json_if_needed(manifest_path, manifest, force=force) == "created" else kept
241
+ bucket.append(manifest_path)
242
+
243
+ return AppContextResult(
244
+ root=airlock_root,
245
+ mode=mode,
246
+ created=tuple(created),
247
+ updated=tuple(updated),
248
+ kept=tuple(kept),
249
+ specs=tuple(spec_names),
250
+ )
251
+
252
+
253
+ def format_app_context_result(result: AppContextResult) -> str:
254
+ lines = [f"initialized app context {result.root}", f"mode: {result.mode}"]
255
+ for path in result.created:
256
+ lines.append(f"created {path.relative_to(result.root.parent)}")
257
+ for path in result.updated:
258
+ lines.append(f"updated {path.relative_to(result.root.parent)}")
259
+ for path in result.kept:
260
+ lines.append(f"kept {path.relative_to(result.root.parent)}")
261
+ lines.append("seeded_specs: " + (", ".join(result.specs) if result.specs else "none"))
262
+ lines.extend(
263
+ [
264
+ "",
265
+ "next:",
266
+ "1. Treat spec snapshots as app-local references, not canonical specs.",
267
+ "2. Mark each manifest spec as read, write, or read_write for the app.",
268
+ "3. Build app reads and governed submissions through approved Airlock/Snowflake access paths.",
269
+ ]
270
+ )
271
+ return "\n".join(lines)
@@ -21,6 +21,6 @@ def about_text() -> str:
21
21
  return f"""{MCP_MARK.rstrip()}
22
22
 
23
23
  version: {__version__}
24
- commands: init-repo, init, import-spec, clone, rename, archive, restore, list-workspaces, summary, next, check, export-csv, render-sql, self-update
24
+ commands: init-repo, init-app-context, init, import-spec, clone, rename, archive, restore, list-workspaces, summary, next, check, export-csv, render-sql, self-update
25
25
  principle: Codex is the conversation; files are memory; Airlock is authority.
26
26
  """
@@ -16,8 +16,9 @@ This repo is an Airlock MCP workspace for drafting Airlock specs with Codex.
16
16
  The public install surface is Airlock MCP, for example
17
17
  `npx @reunionstudio/airlock-mcp install`. Airlock MCP is the single installed
18
18
  interface for building specs, using specs, pulling and pushing governed data,
19
- and discovering improvements from real use cases. This project repo remains
20
- the durable memory for the specs being drafted.
19
+ building apps or workflows that use existing specs, and discovering
20
+ improvements from real use cases. This project repo remains the durable memory
21
+ for the specs being drafted.
21
22
 
22
23
  ## Repo Naming
23
24
 
@@ -45,7 +46,13 @@ installed Airlock app.
45
46
  ## Starting A Spec Project
46
47
 
47
48
  After bootstrap, welcome the user and orient before creating a workspace. Start
48
- with:
49
+ by asking which delivery mode they want:
50
+
51
+ 1. Spec-first: design governed specs before building the app surface.
52
+ 2. App-first: build from existing specs.
53
+ 3. Co-development: develop the app and specs together.
54
+
55
+ For spec-first or co-development work, ask:
49
56
 
50
57
  What process do you want to improve?
51
58
 
@@ -79,10 +86,48 @@ plan for more. Do not create the first workspace until the user chooses a path.
79
86
  Create `posts` only when the user wants a shared feedback loop or explicitly
80
87
  asks for the posts pattern.
81
88
 
89
+ ## Building Apps With Existing Specs
90
+
91
+ Use this path when the user wants to build an app, dashboard, approval queue,
92
+ decision UI, analysis workflow, scheduled agent, or other code that uses specs
93
+ they already have access to. Treat this as app/workflow implementation, not
94
+ spec editing, unless the user explicitly asks to change specs.
95
+
96
+ Use `airlock-mcp init-app-context` in an app repo when the app needs local
97
+ Airlock context. It creates `airlock/specs.manifest.json`,
98
+ `airlock/spec-snapshots/`, `airlock/sample-records/`, and generated helper
99
+ folders. These files are app-local references, not canonical specs. Canonical
100
+ specs live in the specs repo or installed Airlock.
101
+
102
+ Identify:
103
+
104
+ - the app goal and decision the app should support
105
+ - read specs such as budgets, expenses, requests, forecasts, observations,
106
+ payouts, or reference data
107
+ - write specs such as decisions, approvals, comments, commitments, actions, or
108
+ follow-ups
109
+ - where the app should run, such as Streamlit, web app, CLI, notebook,
110
+ scheduled agent, or existing app framework
111
+ - the Airlock access path available in this environment
112
+ - identity, evidence, timestamp, approval, and separation-of-duties rules
113
+
114
+ The app may orient the user with summaries, comparisons, rankings, exception
115
+ queues, proposals, or dashboards. It should submit governed choices back
116
+ through an Airlock spec contract. Do not write directly to Airlock-owned tables,
117
+ stages, generated views, or generated tables. Do not bypass spec workflow just
118
+ because the app can connect to Snowflake. If no suitable decision or action spec
119
+ exists, explain the gap and propose a small spec-design step.
120
+
121
+ In co-development mode, keep two tracks visible:
122
+
123
+ - Spec track: row grain, columns, samples, access, validation, workflow
124
+ - App track: screens, reads, decisions, writes, user actions, runtime
125
+
82
126
  ## Working Style
83
127
 
84
128
  - Use the repo-scoped `$airlock-mcp` skill for spec drafting, review, and
85
- pattern selection.
129
+ pattern selection, or for app/workflow implementation that uses existing
130
+ Airlock specs.
86
131
  - Keep drafts small and concrete. Prefer one useful governed output over a
87
132
  large speculative process map, then keep a plan for later specs.
88
133
  - Preserve decisions in workspace files so future Codex sessions can resume
@@ -5,6 +5,7 @@ import sys
5
5
  from pathlib import Path
6
6
 
7
7
  from . import __version__
8
+ from .app_context import APP_CONTEXT_MODES, format_app_context_result, init_app_context
8
9
  from .art import about_text
9
10
  from .bootstrap import bootstrap_repo, format_bootstrap_result
10
11
  from .jsonio import read_json
@@ -64,6 +65,17 @@ def init_repo(args: argparse.Namespace) -> int:
64
65
  return 0
65
66
 
66
67
 
68
+ def init_app(args: argparse.Namespace) -> int:
69
+ result = init_app_context(
70
+ Path(args.path),
71
+ mode=args.mode,
72
+ spec_sources=[Path(source) for source in args.spec],
73
+ force=args.force,
74
+ )
75
+ print(format_app_context_result(result))
76
+ return 0
77
+
78
+
67
79
  def init_workspace(args: argparse.Namespace) -> int:
68
80
  patterns = load_patterns(repo_root())
69
81
  pattern = patterns[args.pattern]
@@ -308,6 +320,26 @@ def build_parser() -> argparse.ArgumentParser:
308
320
  init_repo_parser.add_argument("--force", action="store_true", help="Overwrite AGENTS.md and the repo-scoped skill.")
309
321
  init_repo_parser.set_defaults(func=init_repo)
310
322
 
323
+ init_app_parser = subparsers.add_parser(
324
+ "init-app-context",
325
+ help="Prepare an app repo airlock/ context with spec snapshots and a manifest.",
326
+ )
327
+ init_app_parser.add_argument("path", nargs="?", default=".", help="App repo path. Defaults to current directory.")
328
+ init_app_parser.add_argument(
329
+ "--mode",
330
+ choices=APP_CONTEXT_MODES,
331
+ default="app-first",
332
+ help="Development mode. Defaults to app-first.",
333
+ )
334
+ init_app_parser.add_argument(
335
+ "--spec",
336
+ action="append",
337
+ default=[],
338
+ help="Spec workspace directory or spec JSON file to snapshot. Repeat for multiple specs.",
339
+ )
340
+ init_app_parser.add_argument("--force", action="store_true", help="Overwrite app context files.")
341
+ init_app_parser.set_defaults(func=init_app)
342
+
311
343
  init_parser = subparsers.add_parser("init", help="Create a spec workspace.")
312
344
  init_parser.add_argument("name", help="Workspace folder name.")
313
345
  init_parser.add_argument(
package/src/mcp.mjs CHANGED
@@ -7,7 +7,7 @@ const GETTING_STARTED_URI = "airlock://getting-started";
7
7
 
8
8
  const START_TOOL = {
9
9
  name: "airlock_start",
10
- description: "Return Airlock MCP setup guidance for building and using Airlock specs.",
10
+ description: "Return Airlock MCP setup guidance for specs and apps that use specs.",
11
11
  inputSchema: {
12
12
  type: "object",
13
13
  properties: {
@@ -44,10 +44,10 @@ export function handleMcpRequest(message) {
44
44
  },
45
45
  serverInfo: {
46
46
  name: "airlock",
47
- version: "0.1.2",
47
+ version: "0.1.3",
48
48
  },
49
49
  instructions:
50
- "Airlock MCP helps agents improve processes with Airlock specs. Use airlock_start for orientation or the airlock_* tools to bootstrap, draft, check, summarize, export, and render specs.",
50
+ "Airlock MCP helps agents improve processes with Airlock specs and build apps or workflows that use existing specs. Use airlock_start for orientation or the airlock_* tools to bootstrap, draft, check, summarize, export, and render specs.",
51
51
  });
52
52
  }
53
53
 
@@ -84,7 +84,7 @@ export function handleMcpRequest(message) {
84
84
  {
85
85
  name: "airlock-start",
86
86
  title: "Start Airlock",
87
- description: "Bootstrap a Git-backed specs repo and choose the first Airlock path.",
87
+ description: "Bootstrap a Git-backed specs repo or choose an Airlock app/spec path.",
88
88
  arguments: [
89
89
  {
90
90
  name: "project",
@@ -103,7 +103,7 @@ export function handleMcpRequest(message) {
103
103
  }
104
104
  const project = params?.arguments?.project || "Home";
105
105
  return makeResponse(id, {
106
- description: "Start building and using Airlock specs in a Git-backed specs repo.",
106
+ description: "Start building specs or apps that use Airlock specs.",
107
107
  messages: [
108
108
  {
109
109
  role: "user",
package/src/text.mjs CHANGED
@@ -25,19 +25,23 @@ Set up this project as an Airlock specs repo. If this project is not already a
25
25
  Git repo, recommend storing it in version control, preferably GitHub if that is
26
26
  available. If you are creating the repo for me, ask where the ${repoName}
27
27
  directory should live before making it, then offer to initialize git and create
28
- or push a GitHub repo.
29
-
30
- Welcome me by asking what process I want to improve. Explain that Airlock works
28
+ or push a GitHub repo. If I am in an app repo and want to build software that
29
+ uses existing specs, or develop the app and specs together, do not bootstrap a
30
+ specs repo unless I ask for spec edits.
31
+
32
+ Welcome me by asking whether I want spec-first, app-first, or co-development:
33
+ spec-first designs governed specs before the app, app-first builds from existing
34
+ specs, and co-development evolves the app and specs together. For spec-first or
35
+ co-development, ask what process I want to improve. Explain that Airlock works
31
36
  best when we can identify the loop around that process: what information comes
32
37
  in, what context helps us understand it, what decision needs to be made, and
33
38
  what action happens after the decision. Information may come from apps, files,
34
- forms, people,
35
- emails, calls, mail, websites, APIs, data feeds, or physical events. Actions
36
- may go back through those same places. Ask whether I already have artifacts:
37
- CSV or Excel files, JSON samples, API docs, schemas, forms, screenshots, PDFs,
38
- exports, message examples, or other content people already use. Treat a small
39
- real sample as stronger evidence than a long explanation, and remind me to
40
- redact secrets.
39
+ forms, people, emails, calls, mail, websites, APIs, data feeds, or physical
40
+ events. Actions may go back through those same places. Ask whether I already
41
+ have artifacts: CSV or Excel files, JSON samples, API docs, schemas, forms,
42
+ screenshots, PDFs, exports, message examples, or other content people already
43
+ use. Treat a small real sample as stronger evidence than a long explanation,
44
+ and remind me to redact secrets.
41
45
 
42
46
  When useful, check the reusable airlock-specs library for starting points,
43
47
  patterns, and ideas, but do not assume those library specs match current
@@ -45,7 +49,15 @@ third-party systems. Prefer current API docs, real exports, samples, and other
45
49
  artifacts when they conflict with a library shape.
46
50
 
47
51
  Ask for the messy version, then help turn it into a small first Airlock spec
48
- and a plan for more. Do not create the first workspace until I choose a path.`;
52
+ and a plan for more. Do not create the first workspace until I choose a path.
53
+
54
+ If I want app-first or co-development, ask for the app goal, the specs I can
55
+ access, which specs are read sources, which spec records decisions or actions,
56
+ and where the app should run. Offer to run \`airlock-mcp init-app-context\` in
57
+ the app repo to seed \`airlock/specs.manifest.json\`, spec snapshots, sample
58
+ records, and generated helper folders. Help code the app using approved
59
+ Airlock/Snowflake access paths. Do not write directly to Airlock-owned tables
60
+ or bypass spec workflow.`;
49
61
  }
50
62
 
51
63
  export function nextSteps(project) {
@@ -63,8 +75,11 @@ ${airlockPrompt(project)
63
75
 
64
76
  Airlock MCP will offer:
65
77
  - process discovery before choosing a spec pattern
78
+ - spec-first, app-first, and co-development planning
66
79
  - spec design with the bundled workbench
67
80
  - Airlock operating patterns for OODA loops and separation of duties
81
+ - app context seeding with spec snapshots and manifests
82
+ - app and workflow coding against existing Airlock specs
68
83
  - observe specs for controlled interface ingestion
69
84
  - orient specs for proposals, context, scoring, or exception queues
70
85
  - decision specs and action specs for governed follow-through
@@ -77,16 +92,19 @@ export function gettingStartedText(project) {
77
92
 
78
93
  Airlock MCP is the single installed interface for AI agents working with
79
94
  Airlock. It helps a person and their agent improve processes by designing
80
- specs, using specs for governed data movement, and planning OODA loops that
81
- can be assisted by people or agents.
95
+ specs, using specs for governed data movement, planning OODA loops, and building
96
+ apps or workflows that read from and submit through existing specs.
82
97
 
83
- Airlock MCP gives agents two kinds of Airlock expertise:
98
+ Airlock MCP gives agents three kinds of Airlock help:
84
99
 
85
100
  1. Spec design: draft, check, revise, import, clone, and prepare specs for
86
101
  installed Airlock validation.
87
102
  2. Airlock operating patterns: use specs to organize observations, orientation,
88
103
  governed decisions, controlled actions, separation of duties, and feedback
89
104
  loops.
105
+ 3. App and workflow implementation: build dashboards, queues, decision UIs,
106
+ analyses, and agent workflows that use existing specs without bypassing
107
+ Airlock contracts.
90
108
 
91
109
  Start in a Git-backed specs repo such as ${specsRepoName(project)}. GitHub is
92
110
  the recommended default when the user has it set up, but any normal repository
@@ -98,7 +116,13 @@ Use this prompt in the specs repo:
98
116
 
99
117
  ${airlockPrompt(project)}
100
118
 
101
- First ask: What process do you want to improve?
119
+ First ask which delivery mode the user wants:
120
+
121
+ 1. Spec-first: design governed specs before building the app surface.
122
+ 2. App-first: build an app or workflow from existing specs.
123
+ 3. Co-development: develop the app and specs together.
124
+
125
+ For spec-first and co-development work, ask: What process do you want to improve?
102
126
 
103
127
  Airlock works best when we can identify the loop around a process:
104
128
 
@@ -123,7 +147,15 @@ current shape of any third-party system. Prefer current API docs, real exports,
123
147
  samples, and other artifacts when they conflict with the library.
124
148
 
125
149
  Give Codex the messy version of the process. Airlock MCP should help turn it
126
- into a small first Airlock spec and a plan for more.`;
150
+ into a small first Airlock spec and a plan for more.
151
+
152
+ For app-first and co-development work, give Codex the app goal and available
153
+ specs. Airlock MCP should identify read specs, write specs, orienting views,
154
+ decision capture, and safe Airlock/Snowflake access paths. It can seed an app
155
+ repo with \`airlock/specs.manifest.json\`, spec snapshots, sample records, and
156
+ generated helper folders. The app should submit decisions, approvals, actions,
157
+ comments, or follow-ups through Airlock spec contracts, not direct table writes.
158
+ In co-development mode, keep the spec track and app track visible side by side.`;
127
159
  }
128
160
 
129
161
  export function helpText() {
@@ -148,5 +180,7 @@ Airlock MCP is the single installed interface for agents working with Airlock.
148
180
  Spec building is bundled inside that experience.
149
181
  Airlock operating patterns help connect specs into OODA loops, separation of
150
182
  duties, governed decisions, controlled actions, and feedback loops.
183
+ Airlock MCP can also help build apps and workflows that use existing specs for
184
+ approved reads and governed submissions.
151
185
  `;
152
186
  }
package/src/workbench.mjs CHANGED
@@ -23,6 +23,8 @@ const workspaceProperty = {
23
23
  maxLength: MAX_ARG_LENGTH,
24
24
  };
25
25
 
26
+ const appModeValues = ["spec-first", "app-first", "co-development"];
27
+
26
28
  export const WORKBENCH_TOOLS = [
27
29
  {
28
30
  name: "airlock_doctor",
@@ -42,6 +44,32 @@ export const WORKBENCH_TOOLS = [
42
44
  cwd: cwdProperty,
43
45
  }),
44
46
  },
47
+ {
48
+ name: "airlock_init_app_context",
49
+ description: "Seed an app repo with airlock/ spec snapshots, samples, generated placeholders, and a manifest.",
50
+ inputSchema: objectSchema({
51
+ path: {
52
+ type: "string",
53
+ description: "App repo path to initialize. Defaults to the current directory.",
54
+ maxLength: MAX_ARG_LENGTH,
55
+ },
56
+ mode: {
57
+ type: "string",
58
+ enum: appModeValues,
59
+ description: "Development mode. Defaults to app-first.",
60
+ },
61
+ specs: {
62
+ type: "array",
63
+ items: {
64
+ type: "string",
65
+ maxLength: MAX_ARG_LENGTH,
66
+ },
67
+ description: "Spec workspace directories or spec JSON files to snapshot.",
68
+ },
69
+ force: forceProperty,
70
+ cwd: cwdProperty,
71
+ }),
72
+ },
45
73
  {
46
74
  name: "airlock_list_patterns",
47
75
  description: "List bundled Airlock spec starter patterns.",
@@ -230,6 +258,28 @@ function optionalEnum(args, name, allowed, fallback) {
230
258
  return value;
231
259
  }
232
260
 
261
+ function optionalStringArray(args, name) {
262
+ const value = args?.[name];
263
+ if (value === undefined || value === null) {
264
+ return [];
265
+ }
266
+ if (!Array.isArray(value)) {
267
+ throw new Error(`${name} must be an array`);
268
+ }
269
+ return value.map((entry, index) => {
270
+ if (typeof entry !== "string") {
271
+ throw new Error(`${name}[${index}] must be a string`);
272
+ }
273
+ if (entry.length > MAX_ARG_LENGTH || /[\u0000-\u001f\u007f]/.test(entry)) {
274
+ throw new Error(`${name}[${index}] must be ${MAX_ARG_LENGTH} characters or fewer with no control characters`);
275
+ }
276
+ if (entry.startsWith("-")) {
277
+ throw new Error(`${name}[${index}] must not start with '-'`);
278
+ }
279
+ return entry;
280
+ });
281
+ }
282
+
233
283
  function requiredEnum(args, name, allowed) {
234
284
  const value = optionalEnum(args, name, allowed, undefined);
235
285
  if (value === undefined) {
@@ -264,6 +314,19 @@ function cliArgsForTool(name, args = {}) {
264
314
  if (force) cliArgs.push("--force");
265
315
  return { cwd, cliArgs };
266
316
  }
317
+ case "airlock_init_app_context": {
318
+ const cliArgs = [
319
+ "init-app-context",
320
+ optionalString(args, "path", "."),
321
+ "--mode",
322
+ optionalEnum(args, "mode", appModeValues, "app-first"),
323
+ ];
324
+ for (const spec of optionalStringArray(args, "specs")) {
325
+ cliArgs.push("--spec", spec);
326
+ }
327
+ if (force) cliArgs.push("--force");
328
+ return { cwd, cliArgs };
329
+ }
267
330
  case "airlock_list_patterns":
268
331
  return { cwd, cliArgs: ["list-patterns"] };
269
332
  case "airlock_show_pattern": {