@mnstry/atelier 0.2.0-alpha.4 → 0.2.0-alpha.6

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.
Files changed (64) hide show
  1. package/CHANGELOG.md +77 -0
  2. package/README.md +61 -18
  3. package/contracts/atelier-repository-observation.v1.schema.json +163 -0
  4. package/contracts/public-api-baseline.json +57 -0
  5. package/docs/assurance-controls.md +41 -0
  6. package/docs/atelier-runtime.md +28 -2
  7. package/docs/atelier-sync.md +171 -0
  8. package/docs/blocks/claims.md +23 -12
  9. package/docs/blocks/will-not-do.md +9 -3
  10. package/docs/design.md +12 -6
  11. package/docs/install.md +26 -4
  12. package/docs/knowledge-graph.md +8 -4
  13. package/docs/local-services.md +101 -0
  14. package/docs/release-engineering.md +86 -12
  15. package/docs/repo-boundary-guard.md +12 -2
  16. package/docs/upgrade.md +40 -2
  17. package/fixtures/atelier-repository-observation/invalid/complete-with-blocker.v1.json +18 -0
  18. package/fixtures/atelier-repository-observation/valid/complete-local.v1.json +48 -0
  19. package/fixtures/projects/sample-workspace/content/source.html.kg.json +4 -1
  20. package/fixtures/projects/source-formats-workspace/content/data.json.kg.json +4 -1
  21. package/fixtures/projects/source-formats-workspace/content/logo.png.kg.json +4 -1
  22. package/fixtures/projects/source-formats-workspace/content/metrics.csv.kg.json +4 -1
  23. package/fixtures/projects/source-formats-workspace/content/pipeline.yaml.kg.json +4 -1
  24. package/package.json +16 -5
  25. package/skills/claude/atelier-local-service/SKILL.md +47 -0
  26. package/skills/claude/atelier-public-boundary/SKILL.md +31 -0
  27. package/skills/codex/atelier-local-service/SKILL.md +47 -0
  28. package/skills/codex/atelier-public-boundary/SKILL.md +31 -0
  29. package/src/boundary/content-rules.mjs +283 -20
  30. package/src/boundary/policy.mjs +162 -72
  31. package/src/cli/execute-command.mjs +36 -0
  32. package/src/cli/run.mjs +35 -7
  33. package/src/collaboration/event-ledger.mjs +365 -0
  34. package/src/collaboration/index.mjs +17 -0
  35. package/src/collaboration/proposals.mjs +265 -65
  36. package/src/commands/attestation.mjs +20 -6
  37. package/src/commands/disclosure.mjs +133 -0
  38. package/src/commands/distribution.mjs +2 -1
  39. package/src/commands/extension-pack.mjs +2 -1
  40. package/src/commands/init.mjs +2 -1
  41. package/src/commands/server.mjs +1 -4
  42. package/src/commands/sync.mjs +100 -0
  43. package/src/contracts/corpus.mjs +6 -0
  44. package/src/disclosure/content-scan.mjs +193 -0
  45. package/src/egress/check.mjs +7 -38
  46. package/src/egress/forbidden-egress.mjs +32 -18
  47. package/src/graph/graph.mjs +112 -314
  48. package/src/graph/knowledge-graph.mjs +94 -18
  49. package/src/harness/context-client.mjs +9 -1
  50. package/src/index.mjs +41 -0
  51. package/src/project/config.mjs +89 -28
  52. package/src/project/file-class.mjs +14 -0
  53. package/src/project/package-root.mjs +10 -0
  54. package/src/project/path-match.mjs +38 -15
  55. package/src/project/private-state.mjs +110 -0
  56. package/src/runtime/git-adapter.mjs +189 -0
  57. package/src/runtime/local-state.mjs +439 -0
  58. package/src/runtime/repository-observation.mjs +491 -0
  59. package/src/runtime/supervisor.mjs +788 -0
  60. package/src/server/local-sidecar.mjs +81 -59
  61. package/src/server/security.mjs +89 -4
  62. package/src/server/server.mjs +3 -2
  63. package/src/support/feedback-report.mjs +4 -3
  64. package/src/upgrade/upgrade.mjs +2 -1
@@ -0,0 +1,171 @@
1
+ # Atelier Sync: Deliverable Zero
2
+
3
+ Atelier Sync is the collaborator-facing name for the resident repository
4
+ supervisor. Deliverable Zero is deliberately headless: it proves the Git,
5
+ filesystem, state-machine, recovery, and user-authority boundaries before a
6
+ tray shell or installer is allowed to make the product feel finished.
7
+
8
+ This is repository mechanics, not MNSTRY runtime authority. A Git commit
9
+ changes the enrolled repository. It does not mutate MNSTRY identity, consent,
10
+ visibility, provisioning, commerce, sessions, audit, or any other managed
11
+ runtime object.
12
+
13
+ ## Authority contract
14
+
15
+ - Enrollment names exactly one repository. Atelier never scans a home folder.
16
+ - One resolved absolute system Git executable, version, and executable digest
17
+ owns Git semantics for the enrollment. Every inherited `GIT_*` variable is
18
+ stripped from supervised Git calls before Atelier adds its small fixed
19
+ safety environment. Custom SSH transport belongs in the user's SSH config;
20
+ credentials continue through ordinary Git credential helpers. Git `2.40.0`
21
+ or newer is required so global and system attribute provenance is observable.
22
+ - Every cycle observes the full repository. There is no watcher correctness
23
+ dependency in Deliverable Zero.
24
+ - Fetch and fast-forward-only reconciliation are mechanical operations.
25
+ - Commit creation is a two-phase, user-confirmed operation. Planning records
26
+ the head, branch, complete status digest, exact file paths, commit message,
27
+ reviewed blob/mode manifest, diff summary, and exact optional upstream push
28
+ identity. The push identity digest covers the single resolved, normalized,
29
+ credential-free execution destination; persisted display evidence likewise
30
+ strips authentication material, query strings, and fragments. Multiple push
31
+ URLs and `url.*.insteadOf`/`pushInsteadOf` rewrites are refused as ambiguous.
32
+ Every authoritative field is bound into the operation id.
33
+ Execution requires that exact id and refuses if the plan, repository,
34
+ staged bytes/modes, written tree, commit parent/message, or publish target
35
+ changed. A publish plan is refused while any earlier local commit remains
36
+ unpublished, and publication names the exact verified commit object rather
37
+ than a movable `HEAD` ref. Plans expire after 24 hours, are consumed by a
38
+ definitive execution attempt, and are held under resident file/count
39
+ ceilings. Expired, malformed, or oversized retained plan files are removed
40
+ under the repository lock before those ceilings are enforced; redirected
41
+ plan state remains a hard refusal.
42
+ - A commit plan cannot absorb pre-existing staged work. It stages only literal,
43
+ explicitly reviewed paths.
44
+ - Configured Atelier boundary policy is checked against the staged change set
45
+ before commit creation using the enrolled Git executable. When that policy
46
+ declares private-domain ownership, actor verification is blocking even in
47
+ legacy-warning mode. The Sync path disables the boundary command's optional
48
+ network `gh api user` fallback and fails closed when local actor evidence is
49
+ insufficient. Commit history is provenance, not current-user identity, and
50
+ is not accepted as actor evidence on this path. Policies without a declared private-domain owner do not invent
51
+ an actor requirement. Ordinary Git hooks still run; the resulting commit tree,
52
+ single parent, and message must equal the reviewed authority or the local
53
+ commit is rolled back and publication is refused.
54
+ - Push is present only when the reviewed plan requested it, the branch had no
55
+ prior unpublished commits, and HEAD still names the exact verified commit.
56
+ Atelier re-resolves the single push URL immediately before publication and
57
+ pushes the exact commit object directly to that reviewed destination. Push is
58
+ never forced, never follows tags, and never recursively publishes submodule
59
+ refs. When fetch and push resolve to the same credential-free identity,
60
+ Atelier refreshes the exact remote-tracking branch and re-observes before it
61
+ reports `committed-and-published`. A distinct configured push URL is honored,
62
+ but remains an explicit attention state because it cannot prove the fetch
63
+ upstream synchronized. A failed push or post-push tracking refresh preserves
64
+ the local commit, creates one stable attention state, and returns a non-zero
65
+ command exit.
66
+ - Semantic conflict resolution, merge commits, rebase, reset, force push,
67
+ browser apply, broad path scans, telemetry, and hidden upload are absent.
68
+
69
+ ## Repository completeness
70
+
71
+ `atelier sync status` emits a supervisor envelope whose `state.observation`
72
+ contains the current `atelier-repository-observation@v1` document. That
73
+ observation cannot report `complete: true` when any of these are unresolved:
74
+
75
+ - lexically identifiable provider-managed, UNC/network, WSL-cross-boundary, or
76
+ unclassified external filesystem roots (mapped-drive classification remains
77
+ an operating-system integration concern for the signed beta);
78
+ - an unsupported Git engine or bare repository;
79
+ - sparse checkout, partial clone, or shallow repository state;
80
+ - tracked paths carrying `assume-unchanged` or `skip-worktree` index flags;
81
+ - missing or unhealthy submodules;
82
+ - required Git LFS content without a working LFS integration, including LFS
83
+ semantics declared by tracked or untracked worktree attributes, repository
84
+ info attributes, and default global or system attributes;
85
+ - an unclassified custom clean, smudge, or process filter;
86
+ - a configured `core.hooksPath` whose executable behavior is outside the
87
+ reviewed repository contract;
88
+ - a remote URL whose authentication shape cannot be classified;
89
+ - multiple push destinations or any configured Git URL rewrite rule;
90
+ - any required Git evidence read that fails, times out, exceeds its budget, or
91
+ cannot be parsed.
92
+ - a change set above the 4,096-entry resident observation ceiling; or
93
+ - a `core.attributesFile` outside the repository-owned/tracked attributes
94
+ boundary whose filter semantics have not been classified.
95
+
96
+ HTTPS through Git Credential Manager, SSH through the user's existing SSH
97
+ configuration, and local test remotes are classified explicitly. Atelier does
98
+ not collect or store provider credentials.
99
+
100
+ ## Local operation state
101
+
102
+ Ignored `.atelier-local/runtime/` contains:
103
+
104
+ - `enrollment.json` — exact repository and Git engine;
105
+ - `state.json` — a bounded projection of healthy, attention, or paused state;
106
+ - `control.json` — user pause/freeze state;
107
+ - `plans/` — expiring, consumed reviewed commit plans under count/byte ceilings;
108
+ - `operations.ndjson` — sequence- and hash-chained resident trace with explicit
109
+ hash-bound checkpoints before its byte or record ceiling, with a fresh
110
+ digest-linked generation after a torn or corrupt chain; and
111
+ - an atomic per-repository operation lock.
112
+
113
+ Every directory component is containment-checked and every state leaf is
114
+ opened without following redirects where the platform supports it, with leaf
115
+ type and identity checks on every platform. Stale-lock recovery uses an
116
+ exclusive recovery claim, ages out an abandoned recovery claim after the owner
117
+ grace interval, identity-checks the lock directory before and after quarantine,
118
+ and quarantines only the claimed stale directory; it never recursively deletes
119
+ a newly acquired lock. A live PID without durable process identity cannot wedge
120
+ the repository forever: its owner record becomes recoverable after the 24-hour
121
+ maximum operation age. Enrollment takes the same lock as every other
122
+ authoritative state mutation.
123
+
124
+ Deleting this directory removes convenience and diagnostics. It cannot change
125
+ repository meaning. Git plus readable files remain authoritative.
126
+
127
+ ## Engineering commands
128
+
129
+ ```bash
130
+ atelier sync enroll --repo /absolute/path/to/repository
131
+ atelier sync status --repo /absolute/path/to/repository
132
+ atelier sync reconcile --repo /absolute/path/to/repository
133
+ atelier sync run --repo /absolute/path/to/repository --once
134
+
135
+ atelier sync plan \
136
+ --repo /absolute/path/to/repository \
137
+ --path docs/decision.md \
138
+ --message "docs: record decision" \
139
+ --publish
140
+
141
+ # Repeat the exact operation id printed by plan:
142
+ atelier sync commit \
143
+ --repo /absolute/path/to/repository \
144
+ --operation operation-... \
145
+ --confirm operation-...
146
+ ```
147
+
148
+ `status`, `audit`, `reconcile`, `run --once`, and `commit` return a non-zero
149
+ process exit when their result is not healthy, so automation cannot treat a
150
+ paused state, attention state, or failed publication as success merely because
151
+ JSON was emitted.
152
+
153
+ The future native shell may label the final two commands **Commit & sync**.
154
+ It must not bypass either phase.
155
+
156
+ The repeated operation id is a visible user-intent confirmation gate, not an
157
+ authorization secret. Local software able to read and modify the repository is
158
+ inside the same operating-system trust domain; the control prevents implicit
159
+ or stale execution, not a hostile process with the user's filesystem access.
160
+
161
+ ## Evidence boundary
162
+
163
+ Deliverable Zero proves the headless supervisor contract on Linux and macOS,
164
+ with Windows CI covering the portable observation, state, and direct-process
165
+ contract. POSIX executable-wrapper substitution is explicitly skipped on
166
+ Windows; native Windows wrapper-injection proof, signed installation,
167
+ background launch at user login, Windows Home/Pro device behavior, macOS
168
+ notarization, and a nontechnical-user workflow belong to the signed
169
+ collaborator beta. Repositories without an initial commit are not supported by
170
+ this deliverable. A Git executable upgrade changes enrolled identity and
171
+ requires re-enrollment.
@@ -1,18 +1,29 @@
1
1
  This package makes three promises. None of them asks for your trust — each
2
2
  one names the command that proves it.
3
3
 
4
- **Nothing leaves your machine, with one exception you can see.** There is no
5
- telemetry, no update check, no crash reporting, and no send path anywhere in
6
- the package. The exception: when no actor is configured, `boundary check` and
7
- `doctor` fall back to the `gh` CLI to resolve your GitHub login, which is an
8
- authenticated request to GitHub made with your own credentials. Set
9
- `MNSTRY_ATELIER_ACTOR` and that path is never taken. The only network client
10
- refuses non-loopback URLs, the served pages carry a policy that authorizes no
11
- external origin, and a fail-closed gate scans the executable and markup files
12
- under `src/`, `bin/`, `scripts/`, and `examples/` for egress primitives. Two
13
- limits worth stating plainly: the gate does not read the `.json` and `.md`
14
- files under `templates/` and `skills/`, and it does not model
15
- `child_process`, which is why the `gh` fallback above does not trip it:
4
+ **There is no silent egress; every network path is named.** There is no
5
+ telemetry, update check, crash reporting, managed-runtime upload, or model
6
+ provider path in the package. The exceptions are explicit: `boundary check`
7
+ may invoke `gh api user` after no declared actor matches an explicit `--actor`,
8
+ `MNSTRY_ATELIER_ACTOR`, `GITHUB_ACTOR`, or a configured Git email; repository
9
+ identity checks may invoke `gh api repos/...` to resolve a canonical GitHub
10
+ identity. Those authenticated requests use your own `gh` credentials. A
11
+ recognized explicit actor prevents the boundary actor fallback; recorded
12
+ repository identities let identity checks keep working when the provider is
13
+ unavailable. Explicitly enrolled Atelier Sync may also run bounded Git fetches
14
+ for observation/reconciliation and one non-force push only when the exact
15
+ reviewed commit plan requested and confirmed it, no earlier local commit is
16
+ waiting to be published, and HEAD still names the verified commit object. Sync
17
+ never uses the network actor fallback, follows tags, or recursively publishes
18
+ submodule refs. The package's HTTP client
19
+ refuses non-loopback URLs, the served pages authorize no external origin, and
20
+ release audit scans every executable or markup file in the exact `npm pack`
21
+ inventory for egress primitives. The standalone gate also scans executable and
22
+ markup files under `src/`, `bin/`, `scripts/`, `templates/`, `examples/`, and
23
+ `skills/`. Two limits worth stating plainly: the egress control does not
24
+ interpret data-only `.json` or `.md` files, and it does not model
25
+ `child_process`; the reviewed `gh` and enrolled Git paths above are documented
26
+ subprocess exceptions rather than scanner detections:
16
27
 
17
28
  ```bash
18
29
  npm run egress:check
@@ -1,7 +1,13 @@
1
1
  - It does not write to a MNSTRY runtime database.
2
- - It does not import, provision, publish, or send anything.
3
- - Except for the documented `gh` actor-resolution fallback, it initiates no
4
- external network requests.
2
+ - It does not import, provision, publish, or send project content to a MNSTRY
3
+ managed runtime.
4
+ - Conformance remains offline. Network access is limited to the documented
5
+ `gh` actor-resolution fallback and explicitly enrolled Atelier Sync Git
6
+ operations: bounded fetch for observation/reconciliation, and non-force push
7
+ only when the exact reviewed commit plan requested and confirmed it, no
8
+ earlier local commit remains unpublished, and HEAD still names the verified
9
+ commit object; Sync does not use network actor fallback, follow tags, or
10
+ recursively publish submodule refs.
5
11
  - It does not execute model-assisted analysis or any model provider.
6
12
  - It does not include client project content.
7
13
 
package/docs/design.md CHANGED
@@ -12,12 +12,16 @@ proves it — because a design document that cannot be checked is marketing.
12
12
 
13
13
  ## 1. A repository with an ontology
14
14
 
15
- A small structured header turns a file into a node: front matter on
15
+ A small structured header turns a file into a governed node: front matter on
16
16
  Markdown, or a `.kg.json` sidecar beside any other format — JSON, YAML,
17
17
  CSV, media, anything — which the kit enrolls without ever parsing the
18
18
  foreign format. The header declares identity (`kg.id`), type, status,
19
19
  audience, and relations to other nodes.
20
20
 
21
+ Markdown without a `kg` block is still counted by the canonical graph engine,
22
+ but only as private `unclassified` material with a diagnostic. Once a `kg`
23
+ block is present, partial or malformed declarations are blocking errors.
24
+
21
25
  The graph builder compiles the repository into a knowledge graph,
22
26
  deterministically: same tree in, same graph out, and `--check` mode fails
23
27
  when a generated graph has drifted from its sources. There is no import
@@ -92,14 +96,16 @@ atelier lock check --project ./atelier.project.json
92
96
  The same governed workspace projects two ways.
93
97
 
94
98
  For humans: a generated review surface, served by a sidecar that binds to
95
- loopback only. The served pages carry a policy that authorizes no external
96
- origin, and the only network client in the package refuses non-loopback
97
- URLs.
99
+ loopback only. It requires a generated publication manifest and serves only
100
+ enrolled safe static files after realpath validation. The served pages carry a
101
+ policy that authorizes no external origin, and the only network client in the
102
+ package refuses non-loopback URLs.
98
103
 
99
104
  For agents: session-bound context and capability envelopes that hand an
100
105
  agent harness a governed view of the workspace — what exists, what it may
101
- look at, what it may propose. Proposals are recorded as metadata; there
102
- are no browser apply endpoints and no write authority to grant. Context
106
+ look at, what it may propose. Proposals are recorded as typed copy-only
107
+ metadata; capability declarations expose no apply endpoint, source write,
108
+ execution, or Git authority. Context
103
109
  without authority. Neutral Claude and Codex skill wrappers ship in the
104
110
  package, so a harness can do readiness review work against the workspace
105
111
  without a single line of custom glue.
package/docs/install.md CHANGED
@@ -30,19 +30,19 @@ The registry is the distribution channel of record, as `docs/continuity.md`
30
30
  commits:
31
31
 
32
32
  ```bash
33
- npm install --save-dev @mnstry/atelier@0.2.0-alpha.4
33
+ npm install --save-dev @mnstry/atelier@0.2.0-alpha.6
34
34
  ```
35
35
 
36
36
  Installing from the matching Git tag resolves to the same reviewed commit:
37
37
 
38
38
  ```bash
39
- npm install --save-dev "git+https://github.com/MNSTRY/atelier.git#v0.2.0-alpha.4"
39
+ npm install --save-dev "git+https://github.com/MNSTRY/atelier.git#v0.2.0-alpha.6"
40
40
  ```
41
41
 
42
42
  Or over SSH:
43
43
 
44
44
  ```bash
45
- npm install --save-dev "git+ssh://git@github.com/MNSTRY/atelier.git#v0.2.0-alpha.4"
45
+ npm install --save-dev "git+ssh://git@github.com/MNSTRY/atelier.git#v0.2.0-alpha.6"
46
46
  ```
47
47
 
48
48
  Keep the `@mnstry/` scope — see the command-form note above for why the
@@ -57,6 +57,24 @@ The workspace `atelier.lock.json` should record the resolved version or Git
57
57
  SHA from the install. Treat the tag or version as the friendly handle and the
58
58
  SHA as the review authority.
59
59
 
60
+ ### Adapter runners must bind package identity
61
+
62
+ An early downstream adapter exposed a subtle failure mode worth making a
63
+ general rule: a wrapper that scans arbitrary sibling checkouts and accepts the
64
+ first matching binary can validate against an archived tree while appearing
65
+ current. Adapter and distribution runners must therefore:
66
+
67
+ 1. declare one exact `@mnstry/atelier` version in their package manifest;
68
+ 2. prefer the installed `node_modules/@mnstry/atelier` package over incidental
69
+ sibling checkouts;
70
+ 3. accept an explicit local checkout only when its package name and version
71
+ match the declared dependency;
72
+ 4. run `atelier --version` and `atelier lock check` as part of adapter proof;
73
+ 5. fail closed when the declared version, resolved package, and lock disagree.
74
+
75
+ This rule binds which Atelier implementation ran. It does not make generated
76
+ output authoritative or grant runtime mutation.
77
+
60
78
  Use one private domain repository per user:
61
79
 
62
80
  ```text
@@ -103,7 +121,11 @@ atelier init --template shared-project --target ./project-alpha --actor tenant-u
103
121
 
104
122
  `--actor` rewrites the copied boundary policy actor entry and binds it to the
105
123
  local Git email when available. Use `--github-login` or `--git-email` to set
106
- those values explicitly during onboarding.
124
+ those values explicitly during onboarding. At check time, a declared actor is
125
+ resolved in this order: recognized `--actor`/`MNSTRY_ATELIER_ACTOR`/
126
+ `GITHUB_ACTOR`, configured Git email, then a reviewed `gh api user` fallback.
127
+ An explicit value that is not declared in the policy does not authenticate an
128
+ actor and therefore does not suppress later resolution attempts.
107
129
 
108
130
  Then update:
109
131
 
@@ -7,16 +7,20 @@ projection readiness.
7
7
 
8
8
  ## Source Rules
9
9
 
10
- - Markdown documents use front matter.
10
+ - Markdown documents may use front matter. A document with no `kg` block is
11
+ represented as `unclassified`, assigned the fail-closed `private` audience,
12
+ and reported diagnostically rather than silently treated as governed.
11
13
  - Non-Markdown sources use adjacent `.kg.json` sidecars.
12
- - `kg.id` is required and stable.
13
- - `kg.audience` is required.
14
+ - Once a Markdown `kg` block exists, `kg.id` and `kg.audience` are required;
15
+ empty, partial, and malformed declarations are blocking errors.
14
16
  - `kg.visibility` is invalid in local source metadata.
15
17
  - Declared relations emit semantic edges.
16
18
 
17
19
  ## Source formats
18
20
 
19
- The graph is sidecar-first: Markdown front matter is the only inline metadata
21
+ The graph has one canonical classification engine in
22
+ `src/graph/knowledge-graph.mjs`; the older `graph.mjs` surface is a compatibility
23
+ adapter over it, not a second classifier. Markdown front matter is the only inline metadata
20
24
  format the kit reads, and every other file becomes a first-class node through
21
25
  an adjacent `<file>.kg.json` sidecar
22
26
  (`contracts/knowledge-source-sidecar.v1.schema.json`). This is how any format
@@ -0,0 +1,101 @@
1
+ # Managed local services
2
+
3
+ Atelier-backed repositories sometimes need a local authoring, review, or
4
+ preview service that writes repository-owned state. The service may be
5
+ specialized; its lifecycle and data-boundary behavior should not be improvised.
6
+
7
+ This contract applies to consumer services other than `atelier dev`. It does
8
+ not change the Atelier sidecar's own lifecycle.
9
+
10
+ ## Atelier sidecar reference boundary
11
+
12
+ The built-in `atelier dev` sidecar is the reference for its request and
13
+ publication boundary, not for the managed lifecycle below. It binds only to a
14
+ literal loopback host and refuses startup without a valid generated
15
+ `atelier.manifest.json`. Static reads are limited to manifest-enrolled,
16
+ supported file types after realpath containment; hidden, state, secret-shaped,
17
+ symlinked, unknown, and unenrolled paths are denied. API reads require a trusted
18
+ loopback host and `Sec-Fetch-Site: same-origin` or `none`; `same-site` and
19
+ cross-site requests are refused. Mutations additionally require an
20
+ allowed method, exact expected origin, and the session nonce. Its collaboration
21
+ records are copy-only proposals, not an apply channel.
22
+
23
+ ## Kit and adapter boundary
24
+
25
+ The portable Atelier layer owns these invariants:
26
+
27
+ - bind to loopback only;
28
+ - expose explicit `start`, `status`, and `stop` operations;
29
+ - detach only when the user asked for a service that survives an agent command;
30
+ - identify the running process with both a random runtime identifier and its
31
+ process ID;
32
+ - refuse to adopt, overwrite, or stop a process whose identity cannot be
33
+ proven;
34
+ - keep runtime records, logs, drafts, and recovery copies in ignored local
35
+ storage with owner-only permissions;
36
+ - make writes atomic and keep a failed queued write from poisoning later saves;
37
+ - keep unsaved browser work available when the service disappears;
38
+ - provide an explicit snapshot export and a retry path;
39
+ - use no telemetry, remote dependency, or external send path.
40
+
41
+ The consuming repository owns its service name, port, executable, state schema,
42
+ content, command names, and user-facing recovery copy. None of those adapter
43
+ details belong in the public Atelier kit.
44
+
45
+ ## Lifecycle contract
46
+
47
+ ### Start
48
+
49
+ 1. Resolve an explicit loopback port and ignored runtime directory.
50
+ 2. Probe the service health endpoint.
51
+ 3. If a managed instance answers with the recorded runtime identifier and PID,
52
+ report it as already running.
53
+ 4. If anything else owns the port, refuse to take it over.
54
+ 5. Generate a fresh runtime identifier, start the child without a shell, write
55
+ a mode-0600 runtime record, and wait for health to echo both identifiers.
56
+ 6. If health never proves ownership, stop only the child just created and
57
+ report the ignored operational log.
58
+
59
+ ### Status
60
+
61
+ Status is healthy only when the runtime record, health response, runtime
62
+ identifier, PID, service identity, and loopback address agree. A responding
63
+ port without that proof is occupied, not adopted.
64
+
65
+ ### Stop
66
+
67
+ Stop only the PID whose health response matches the recorded runtime identifier
68
+ and PID. Refuse on disagreement. After a clean stop, remove only the generated
69
+ runtime record. Never kill by port, process name, or broad pattern.
70
+
71
+ Managed start survives a terminal or agent command. It does not imply operating
72
+ system startup. Installing an OS-level service is a separate system change and
73
+ requires explicit user authorization.
74
+
75
+ ## Authoring-state contract
76
+
77
+ - The canonical template may be tracked; the live draft must be ignored unless
78
+ a reviewed workflow explicitly promotes a sanitized artifact.
79
+ - Live state and runtime metadata use owner-only permissions.
80
+ - Browser autosave is a recovery layer, not a substitute for the canonical
81
+ local file.
82
+ - A lost server must produce a persistent, actionable message naming the exact
83
+ restart command, the retained browser state, the snapshot option, and Retry.
84
+ - Conflict responses remain fail-closed. Do not silently overwrite a newer
85
+ file or reconcile divergent tabs automatically.
86
+
87
+ ## Required evidence
88
+
89
+ A consumer implementation is not complete until tests prove:
90
+
91
+ 1. start is idempotent and survives the launching command;
92
+ 2. status refuses an unowned listener;
93
+ 3. stop is bound to runtime identifier and PID;
94
+ 4. a stopped server leaves the exact in-browser draft intact;
95
+ 5. restart plus Retry persists that draft;
96
+ 6. a refused filesystem write does not break later valid saves;
97
+ 7. private state and runtime files are ignored and owner-readable only; and
98
+ 8. a fresh checkout can discover the commands through its agent instructions.
99
+
100
+ Use synthetic fixtures for public Atelier tests. Tenant-specific proof remains
101
+ in the tenant repository.
@@ -10,16 +10,23 @@ a location, never the matched content.
10
10
  ### release:audit
11
11
 
12
12
  `npm run release:audit` (`scripts/check-release-tarball.mjs`) audits the exact
13
- tarball `npm pack` would publish:
13
+ tarball `npm pack` would publish. `npm run release:candidate` packs once and
14
+ passes the same archive and expected SHA-256 through this audit and both
15
+ consumer smokes:
14
16
 
15
17
  - Package metadata: name, license, exposed CLI bins, and a mandatory `files`
16
18
  allowlist in `package.json`.
17
19
  - Tarball allowlist: every packed path must match a known-good pattern; any
18
- unexpected file fails the audit.
20
+ unexpected file fails the audit. The extracted archive inventory must match
21
+ the `npm pack --json` inventory exactly.
19
22
  - Content scan: every packed file is scanned against committed structural
20
23
  patterns (absolute user paths, machine-local temp paths, agent-local state
21
24
  paths, key material, secret-like assignments) and against the maintainer-held
22
25
  denylist described below.
26
+ - Egress scan: the canonical egress control receives the exact `npm pack`
27
+ inventory and inspects every packed executable or markup file, including a
28
+ test-shaped path if one is ever admitted to the artifact. Packed test-fixture
29
+ suppression markers are refused; reviewed local-computed markers are counted.
23
30
  - Version drift: `CHANGELOG.md` must contain a `## <version>` heading and
24
31
  `README.md` must mention the version. The expected version and tarball name
25
32
  are derived from `package.json`, never hardcoded.
@@ -27,6 +34,15 @@ tarball `npm pack` would publish:
27
34
  Exit codes: `0` clean, `1` findings, `2` configuration error (for example an
28
35
  unavailable denylist without explicit acknowledgment).
29
36
 
37
+ `release:candidate` additionally requires a clean tree, records the candidate
38
+ commit and tarball digest, and refuses a version already bound to a different
39
+ tagged commit. It is a release-evidence command, not a publication command.
40
+ When `ATELIER_RELEASE_OUTPUT_DIR` is set, it atomically retains the verified
41
+ tarball, npm-pack metadata, and a machine-readable receipt. The trusted
42
+ publishing workflow runs `prepublishOnly` explicitly with that output directory,
43
+ re-verifies the retained digest, and publishes the retained tarball path rather
44
+ than repacking the source checkout.
45
+
30
46
  ### repo:check
31
47
 
32
48
  `npm run repo:check` (`scripts/check-repo-disclosure.mjs`) sweeps the whole
@@ -43,6 +59,27 @@ Git-tracked tree, not just the tarball:
43
59
  Exit codes match `release:audit`: `0` clean, `1` findings, `2` configuration or
44
60
  usage error.
45
61
 
62
+ ### disclosure check
63
+
64
+ `atelier disclosure check` is the portable consumer-facing content gate. It
65
+ scans every tracked text file by default or the exact staged index with
66
+ `--staged`, never follows tracked symlinks, and reports labels plus locations
67
+ without echoing matched content. `--untrusted` suppresses finding details.
68
+
69
+ The command loads private patterns from `ATELIER_DENYLIST_JSON`, an explicit
70
+ `--denylist` path, or ignored
71
+ `.atelier-local/disclosure-denylist.json`. A repository-local denylist is
72
+ refused if it is tracked or not covered by `.gitignore`. Missing private
73
+ patterns are a configuration failure unless `--structural-only` explicitly
74
+ acknowledges the reduced verdict. Public text-only packaging can also use
75
+ `--fail-on-binary`; Atelier's own tarball audit separately refuses packed
76
+ binary files.
77
+
78
+ The public Atelier repository keeps the stronger `repo:check` identity and
79
+ commit-message controls. The portable command exists so consumer repositories
80
+ can enforce the same public/private content boundary without inheriting
81
+ MNSTRY-specific commit identities.
82
+
46
83
  ### Commit-identity gate
47
84
 
48
85
  Part of `repo:check --commits`. Commit authors must match a hardcoded
@@ -65,18 +102,52 @@ changelog and the migration registry honest about breaking changes:
65
102
  - Every registered migration record is re-validated against the migration
66
103
  record contract.
67
104
 
105
+ ### public-api:compat
106
+
107
+ `npm run public-api:compat` compares every package subpath and named JavaScript
108
+ export published by `v0.2.0-alpha.4` with the current package. Removing one
109
+ fails closed. It also rejects modified source that retains the already-tagged
110
+ baseline version, independently of the release-candidate check. The baseline
111
+ records and verifies its tag commit and the public npm artifact identity; the
112
+ published archive was independently checked to contain all 20 subpaths and 212
113
+ named exports. This is runtime JavaScript shape continuity, not a promise about
114
+ function signatures, types, or behavior.
115
+
68
116
  ### egress:check
69
117
 
70
118
  `npm run egress:check` (`atelier egress check`) scans package runtime paths for
71
- forbidden non-localhost egress. The package claims no network egress in runtime
72
- paths; this gate is the mechanical check behind that claim.
119
+ forbidden non-localhost HTTP egress primitives. The package claims no silent or
120
+ undocumented runtime egress; this gate is one mechanical check behind that
121
+ claim. It does not interpret data-only Markdown/JSON and does not model
122
+ `child_process`, so the reviewed `gh` identity-resolution subprocesses and the
123
+ explicitly enrolled Atelier Sync Git fetch/non-force-push subprocesses are
124
+ documented exceptions backed by dedicated refusal tests. Sync refuses fetch on
125
+ an incomplete observation, refuses publish while prior local commits are
126
+ unpublished, strips repository/config retargeting environment variables, and
127
+ pushes the exact post-hook-verified commit object without following tags or
128
+ recursively publishing submodule refs. Its boundary check also suppresses the
129
+ optional network actor fallback.
73
130
 
74
131
  ### consumer:smoke
75
132
 
76
133
  `npm run consumer:smoke` (`scripts/consumer-smoke.mjs`) packs the real tarball,
77
134
  installs it offline into a throwaway consumer project with lifecycle scripts
78
- disabled, and imports the public API to validate a sample export. It proves the
79
- tarball is installable and functional exactly as a consumer receives it.
135
+ disabled and without copying the publisher's `overrides`, checks the installed
136
+ dependency tree, imports every declared JavaScript export, parses every
137
+ declared JSON export, and validates a sample export through both API and CLI.
138
+ It proves the tarball is installable and functional exactly as a bare consumer
139
+ receives it from disk. The offline smoke deliberately warms the locked registry
140
+ dependency closure first; it does not prove that a not-yet-published version
141
+ resolves from a cold or live registry.
142
+
143
+ ### assurance:mutation-smoke
144
+
145
+ `npm run assurance:mutation-smoke` runs local, synthetic negative controls for
146
+ the boundary, graph, packed and direct egress, sidecar, and distribution
147
+ families. Every fixture introduces one named failure and must be refused by the
148
+ terminal CLI or release command where one exists. This is part of
149
+ `prepublishOnly`; a control that stops detecting its failure mode blocks the
150
+ release even if positive-path tests remain green.
80
151
 
81
152
  ### Egress-marker inventory
82
153
 
@@ -105,7 +176,7 @@ maintainer-reviewed before entering the list.
105
176
  Four jobs run on pushes to `main` and on pull requests, and all four are
106
177
  required status checks:
107
178
 
108
- - `test`: syntax check (`node --check`, not a type system), the full test suite, contract checks, `egress:check`, and
179
+ - `test`: syntax check (`node --check`, not a type system), the full test suite (including isolated consumer disclosure-command proofs), contract checks, `egress:check`, `assurance:mutation-smoke`, and
109
180
  `migrations:check`. This job sets `ATELIER_ALLOW_MISSING_DENYLIST=1` scoped
110
181
  to the job only — denylist assertions belong to the secret lane.
111
182
  - `consumer-smoke`: warms the npm cache with `npm ci` (the offline tarball
@@ -127,11 +198,14 @@ the only safe block.
127
198
 
128
199
  To clear a fork pull request, a maintainer verifies the head SHA and dispatches
129
200
  the `fork-sweep` workflow with the pull-request number and that SHA. The
130
- workflow runs the trusted scanner from `main` against the fork's tree checked
131
- out as data — it never installs or executes anything from the untrusted tree —
132
- re-verifies that the head SHA has not moved, and posts a `secret-sweep` check
133
- run on the SHA. The newest check run with that name supersedes the earlier
134
- failure for branch protection.
201
+ workflow pins the trusted scanner checkout to the protected default branch and
202
+ runs it against the fork checked out as data — it never installs or executes
203
+ anything from the untrusted tree. It requires an open PR whose base repository
204
+ and branch are the protected default, pins the base and head SHAs from GitHub,
205
+ scans the complete bounded base-to-head object range while waiving only
206
+ repository-specific commit identity, re-verifies that the head SHA has not
207
+ moved, and posts a `secret-sweep` check run on the SHA. The newest check run
208
+ with that name supersedes the earlier failure for branch protection.
135
209
 
136
210
  `release:audit` never runs against untrusted code (`npm pack` executes
137
211
  lifecycle scripts); it re-runs on the push build of the merge commit. No
@@ -170,12 +170,22 @@ machine for weeks.
170
170
  - `pre-push` runs `atelier boundary push-check` — git writes the ref updates to
171
171
  the hook's stdin, and only that range is judged. A brand-new branch is diffed
172
172
  against the empty tree, so nothing slips through unscanned.
173
- - `atelier boundary audit` scans the whole tree and **reports without blocking**,
174
- listing both matches and declared exceptions with their reasons.
173
+ - `atelier boundary audit` scans the current working tree by default and
174
+ **reports without blocking**, listing matches, incomplete-read diagnostics,
175
+ and declared exceptions with their reasons. Use `--head` for a committed
176
+ snapshot. The output names its source so a dirty tree cannot be mistaken for
177
+ `HEAD` evidence.
175
178
 
176
179
  If the guard cannot work out which repo it is running in, it fails closed. A
177
180
  guard that silently judges nothing is worse than one that stops you.
178
181
 
182
+ Path scopes are segment-aware globs: `*` does not cross `/`, `**` does, and
183
+ patterns are matched against normalized repository-relative paths. Explicitly
184
+ declaring `contentRules: []` is invalid; omit the field to receive defaults.
185
+ Git diff output and binary reads have per-file and aggregate budgets. A failed,
186
+ truncated, oversized, or unparsable evidence read produces a blocking
187
+ completeness diagnostic rather than a partial clean verdict.
188
+
179
189
  ### Declaring an exception
180
190
 
181
191
  Exceptions live in the boundary policy, not in the guard script. A repo-specific