@nurix/etna 0.15.5 → 0.15.7

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,5 +1,5 @@
1
1
  {
2
- "fingerprint": "fcd26215a41d",
2
+ "fingerprint": "ea9d56be1c3e",
3
3
  "plugins": {
4
4
  "code": "code",
5
5
  "kb-agi": "kb-agi",
@@ -1,7 +1,7 @@
1
1
  # Product Reverse-Engineering — Orchestrator
2
2
 
3
3
  This is the **policy** half of the `re` harness. The plugin ships the capabilities (11
4
- dimension-dumper agents + 3 `re-*` rules, one per pipeline phase); this file drives them. Together they reverse-engineer a
4
+ dimension-dumper agents + 4 `re-*` rules, one per mode); this file drives them. Together they reverse-engineer a
5
5
  product across every surface it exposes and dump a structured, provenance-tracked corpus into
6
6
  `research/<target>/`.
7
7
 
@@ -81,8 +81,11 @@ method · confidence · completeness · gaps) + `raw/`, obeying `.claude/rules/r
81
81
  is authorized, **dispatch the non-browser dumpers in the background (`run_in_background`) and drive
82
82
  the gated browser session in the main session concurrently** — they don't contend (Chrome belongs to
83
83
  the main session; the dumpers use WebFetch/curl), so the whole of Ingestion runs in parallel.
84
- - **Shared seam** — bundle/session/wire each contribute to the canonical `raw/api-path-catalog.md`
85
- with a `source:` column; don't triplicate.
84
+ - **Shared seam** — **seed the two shared files `dimensions/_shared/{api-path-catalog.md,
85
+ feature-flags.md}` (empty, with a header) once, before the dumper fan-out**; bundle/session/wire/
86
+ distribution then each *append* a `source:`-tagged section to the same physical file (never their own
87
+ copy). One owner dimension per static asset; a second mine of the same asset is one source, not
88
+ corroboration (`re-ingestion` §6).
86
89
 
87
90
  Validate each return (`_summary.md` + provenance present). A `blocked`/`partial` return is recorded,
88
91
  not blindly re-run.
@@ -101,19 +104,35 @@ high→fact, low/single-source→tentative, conflict→flagged. The four rollups
101
104
 
102
105
  ---
103
106
 
104
- ## Mode 4 — Self-correction (the harness improves itself, never silently)
105
-
106
- Read the finished run (scope verdict, plan, every dimension `_summary.md`, the rollups) and detect
107
- where the harness under-delivered (method ≠ planned, missing artifacts, low completeness, blocked
108
- where predicted available). Write **one** file — `research/<target>/04-self-correction-proposal.md`
109
- — an ordered table of proposed fixes, each routed to its target file (`re-discovery` /
110
- `re-ingestion` / `re-evaluation` / an agent), with severity, an
111
- additive/structural flag, and a `research/<target>/…` evidence anchor.
112
-
113
- **Approval gate:** present the table and STOP — nothing is applied. The user accepts / rejects /
114
- defers per item. Then the **main session** (not a sub-agent) applies accepted items, flips each row
115
- to APPLIED, and logs the change. Promote only what the *next, different* target would also hit;
116
- prefer additive edits; structural edits are flagged, never auto-applied.
107
+ ## Mode 4 — Self-correction (the run corrects itself; the harness improves only on approval)
108
+
109
+ Mode 4 is a loop, not a one-shot. Follow `.claude/rules/re-self-correction.md` — it carries the
110
+ **defect checklist** (A), the **measurement rubric** (B), and the **iterate-or-propose loop** (C).
111
+ Three stages, driven by the **main session** (not a sub-agent):
112
+
113
+ 1. **Inspect** the finished run (scope verdict, plan, every dimension `_summary.md`, the rollups)
114
+ against the **built-in defect checklist** → a concrete list of *behavioral defects* in the prior
115
+ steps (mode · defect · severity · evidence anchor · within-run|harness-changing).
116
+ 2. **Measure** the run against the **rubric** → a `/100` score + vibe band. Always — every iteration,
117
+ including the final one; the self-correcting step ALWAYS evaluates the current run.
118
+ 3. **Iterate-or-propose**, splitting each defect by one test — *does the fix require editing a `re-*`
119
+ rule or an agent?*
120
+ - **No → within-run:** the main session **auto-iterates** (no approval), re-running the cheapest
121
+ affected Mode 1–3 steps on THIS target and re-measuring each round. The loop is **capable of ≥5
122
+ iterations** and **stops on regression** (a clear score drop → revert to the best prior run),
123
+ **convergence** (no within-run defects left), or the cap. **Read-only / no-cost only** — any fix
124
+ needing a Pass-2 / credit-spend / state-change / credential action goes through the
125
+ explicit-confirmation gate, never the auto-loop.
126
+ - **Yes → harness-changing:** accumulate into `research/<target>/04-self-correction-proposal.md`
127
+ (the shape is in `re-self-correction.md`: run measurement · iteration ledger · defect list ·
128
+ within-run fixes applied · the approval-gated harness-change table).
129
+
130
+ **Approval gate (unchanged):** present the harness-change table + the iteration ledger and STOP —
131
+ nothing in the harness is applied. The user accepts / rejects / defers per item. Then the **main
132
+ session** (not a sub-agent) applies accepted items, flips each row to APPLIED, and logs the change.
133
+ Promote only what the *next, different* target would also hit; prefer additive edits; structural edits
134
+ are flagged, never auto-applied. (For a batch, additionally roll up into a cross-target synthesis that
135
+ promotes only patterns ≥2 *independent* targets hit — a homogeneous batch's 6/6 is one vote, not six.)
117
136
 
118
137
  ---
119
138
 
@@ -27,6 +27,12 @@ the endpoints it will call, and the flags it gates behind.
27
27
  pull every `<script src>` and `<link rel=modulepreload>`. Vite/Next ship hashed
28
28
  `/assets/*.js` and `/_next/static/chunks/*.js`. Grab the entry chunk + the largest vendor
29
29
  chunk; `curl -s <chunk-url> -o raw/.work/<name>.js`. Note `Content-Length` per chunk.
30
+ **Bound this — do NOT exhaustively fetch every lazy chunk.** The route table and the GraphQL/op
31
+ registry live in the entry + `_app` + a registry chunk; grab those plus the largest few vendor
32
+ chunks (cap ~40 chunks / ~5 MB / 90 s), and record the rest as a `gap`. Fetching all lazy chunks
33
+ (often 90+, multi-MB) can run 3–4× a sibling dumper and read as hung. **Per §8, write a
34
+ `status: partial` `_summary.md` from the entry/manifest chunks BEFORE the wider fetch loop**, then
35
+ enrich and flip to `complete`.
30
36
  2. **Check for source maps FIRST.** Grep each chunk for `//# sourceMappingURL=`. If present,
31
37
  fetch the `.map` and reassemble original sources (`npx source-map-explorer` or unpack
32
38
  `sourcesContent` from the JSON map). A live map flips you to `method: source-map-reassembly,
@@ -35,13 +41,16 @@ the endpoints it will call, and the flags it gates behind.
35
41
  manifests (`__BUILD_MANIFEST`, `/_next/static/.../_buildManifest.js`), lazy-import chunk maps.
36
42
  `grep -oE '"/[a-z][a-z0-9/:_-]*"'` over the de-minified source, then de-dup → `raw/route-table.md`.
37
43
  4. **API-path catalog (SHARED seam — §6).** Mine fetch/axios base URLs and path literals
38
- (`/api/`, `/graphql`, `${API_BASE}` template fragments). Write rows to the **shared**
39
- `raw/api-path-catalog.md` with a `source: bundle` column — do NOT invent a private name; the
40
- `session` and `wire-capture` dumps append to the same file so evaluation can run its three-way
41
- diff. A path here that `session`/`wire` never hit is a dormant/unshipped route (a finding).
44
+ (`/api/`, `/graphql`, `${API_BASE}` template fragments). **Append** rows to the **one physical
45
+ shared file** `research/<target>/dimensions/_shared/api-path-catalog.md` with a `source: bundle`
46
+ column — do NOT create a copy under your own `raw/`; `session`/`wire`/`distribution` append to the
47
+ *same* file so evaluation runs its adaptive diff. A path here that `session`/`wire` never hit is a
48
+ dormant/unshipped route (a finding). **You OWN the app-API bundle mine** (re-ingestion §6): when
49
+ `source:none` and no machine spec is served, other dimensions cite your catalog by path and do not
50
+ re-mine the same bundle (a second mine is one source, never corroboration).
42
51
  5. **Feature flags (SHARED).** Find flag SDK keys and flag literals (LaunchDarkly `client-side-id`,
43
52
  Statsig/Flagsmith/PostHog keys, `flags[` / `isEnabled('...')` / `experiment` string literals).
44
- Write to the **shared** `raw/feature-flags.md` with `source: bundle`.
53
+ **Append** to the shared `research/<target>/dimensions/_shared/feature-flags.md` with `source: bundle`.
45
54
  6. **Embedded env config.** Grep `VITE_`, `NEXT_PUBLIC_`, `REACT_APP_`, `import.meta.env`,
46
55
  `window.__ENV__`. **Field-names only — REDACT every value** to `<REDACTED:env-value>` per §7
47
56
  (publishable keys, analytics IDs, base URLs are field-name-worthy; treat any value as a secret
@@ -73,17 +82,20 @@ gaps: [<unmined chunks, absent source maps, obfuscated route tables, ...>]
73
82
 
74
83
  Then Method / Findings (framework, build tool, route count, endpoint count — tables) / Inferences
75
84
  (public-vs-gated routes, the backend the SPA expects, which third-party SDKs are wired) / Open
76
- questions / Artifacts. Plus the `raw/` files named in `re-discovery` Part C (deployed-client-bundle):
77
- `raw/{route-table.md, api-path-catalog.md, feature-flags.md, env-config.md, bundle-map.md}` —
78
- each prose artifact carrying the `<!-- source: ... · captured_at: ... · method: ... -->` line.
85
+ questions / Artifacts. Dimension-private `raw/` files: `raw/{route-table.md, env-config.md,
86
+ bundle-map.md}` — each prose artifact carrying the `<!-- source: ... · captured_at: ... · method: ... -->`
87
+ line; the API-path catalog and feature flags are **appended to the shared
88
+ `dimensions/_shared/{api-path-catalog.md, feature-flags.md}`**, which your `_summary.md` Artifacts list
89
+ *links* (not duplicates).
79
90
 
80
91
  ## Return value
81
92
 
82
93
  Return a one-paragraph headline of what the deployed shell reveals — framework + build tool, route
83
94
  count, the shape of the API surface it calls, whether source maps were shipped (and the confidence
84
- that implies). **Cross-checks:** flag that `raw/api-path-catalog.md` and `raw/feature-flags.md` are
85
- the SHARED seam artifacts (§6) — `session` and `wire-capture` write to the same files with
86
- `source: session | wire`, and evaluation owns the static-vs-observed three-way diff. Surface any
95
+ that implies). **Cross-checks:** flag that `dimensions/_shared/api-path-catalog.md` and
96
+ `dimensions/_shared/feature-flags.md` are the SHARED seam artifacts (§6, one physical file each) —
97
+ `session`/`wire`/`distribution` append to the same files with `source: session | wire | distribution`,
98
+ and evaluation owns the adaptive static-vs-observed diff. Surface any
87
99
  endpoint or flag that looks authed/internal but is statically declared in the unauth bundle — that
88
100
  gap is the dimension's highest-value output.
89
101
 
@@ -46,9 +46,10 @@ Then dispatch by artifact type (do all that apply):
46
46
  - **CLI.** Install / fetch and harvest help text: `<cli> --help`, `<cli> help`, and per-subcommand
47
47
  `<cli> <sub> --help`. The subcommand tree **is** an API catalog. → `raw/cli-catalog.md`.
48
48
 
49
- For each, **also** harvest the API surface and feature flags into the **shared seam artifacts**
50
- (contract §6): `raw/api-path-catalog.md` with a `source: distribution` column, and `raw/feature-flags.md`
51
- if flags ship. Digest large bundles (counts + shape + one redacted sample), never paste (§5).
49
+ For each, **also** harvest the API surface and feature flags by **appending** to the **shared seam
50
+ artifacts** (contract §6, one physical file each): `dimensions/_shared/api-path-catalog.md` with a
51
+ `source: distribution` column, and `dimensions/_shared/feature-flags.md` if flags ship — never a private
52
+ copy. Digest large bundles (counts + shape + one redacted sample), never paste (§5).
52
53
 
53
54
  ## Output
54
55
 
@@ -78,16 +79,16 @@ inference vs. fact), **Open questions** (EULA-restricted decompilation, missing
78
79
 
79
80
  Then write the per-type raw files that apply — `raw/{extension-manifest.md, electron-asar-map.md,
80
81
  mobile-strings.md, cli-catalog.md}` (each with the `<!-- source · captured_at · method -->`
81
- metadata line) — plus `raw/api-path-catalog.md` (`source: distribution`) and, if flags ship,
82
- `raw/feature-flags.md`.
82
+ metadata line) — plus **appends** to the shared `dimensions/_shared/api-path-catalog.md`
83
+ (`source: distribution`) and, if flags ship, `dimensions/_shared/feature-flags.md`.
83
84
 
84
85
  ## Return value
85
86
 
86
87
  Return a **one-paragraph headline** of what the shipped client reveals — its declared hosts,
87
88
  permission surface, embedded vendor SDKs, and how much richer (or not) it is than the web app.
88
- The API paths and feature flags you wrote feed the **seam rule (§6)**: flag `raw/api-path-catalog.md`
89
- with `source: distribution` so evaluation can three-way-diff it against `deployed-client-bundle`
90
- (`source: bundle`), `session`, and `wire-capture`. The hosts and SDK list cross-check
89
+ The API paths and feature flags you appended feed the **seam rule (§6)**: your
90
+ `dimensions/_shared/api-path-catalog.md` rows carry `source: distribution` so evaluation can run its
91
+ adaptive diff against `deployed-client-bundle` (`source: bundle`), `session`, and `wire-capture`. The hosts and SDK list cross-check
91
92
  **infra-backend-fingerprint** (a host the binary calls that DNS/CT didn't surface is a finding);
92
93
  the runtime hosts also seed **wire-capture** targets (the binary says what it *might* call; the
93
94
  capture says what it *does*).
@@ -86,7 +86,7 @@ per §5):
86
86
  Return a **one-paragraph headline** of the reconstructed backend — origin cloud, edge CDN, email +
87
87
  AI + payments providers, and the most interesting CT-leaked host (`staging.`/`internal.`/`admin.`).
88
88
  Cross-check against siblings: the CSP `connect-src` and CT-found `api.<target>` host should match the
89
- **`raw/api-path-catalog.md`** seam (§6) produced by `deployed-client-bundle`/`session`/`wire-capture`
89
+ **`dimensions/_shared/api-path-catalog.md`** seam (§6) produced by `deployed-client-bundle`/`session`/`wire-capture`
90
90
  — flag a host in your fingerprint that none of them ever hit as a dormant/internal surface, and flag
91
91
  a vendor named in `sub-processors.md` (e.g. an AI provider) that no other dimension surfaced. Note
92
92
  any CT-leaked host worth a `wire-capture` or `deployed-client-bundle` follow-up.
@@ -41,10 +41,20 @@ Under `research/<target>/dimensions/session/`:
41
41
  - `captures/p0-surface.md`, `p1-auth.md`, and per-phase narratives (P2 core action, P3
42
42
  streaming, P4 runtime/state, P5 iteration) — decoded, opinionated, per `re-ingestion` §9.5.
43
43
  - `raw/*.json` — verbatim endpoint captures with `_meta` + `_decoded_fields`, **all secrets
44
- redacted** (`re-ingestion` §9.5). Digest bodies >50KB.
44
+ redacted** (`re-ingestion` §9.5). Digest bodies >50KB. App API paths + flags you observe **append
45
+ to the shared `dimensions/_shared/{api-path-catalog.md, feature-flags.md}` with `source: session`**
46
+ (re-ingestion §6) — and the folded rung-1 wire tap appends there as `source: wire`.
45
47
  - `curls.md` — reproducible commands per endpoint, auth model documented (`re-ingestion` §9.5).
48
+ - **Visual / surface map** — `captures/screens/` is preferred *when screenshots are available*; when
49
+ they are **not** (no image tool, or the app holds a persistent connection that never reaches
50
+ page-idle), a textual `captures/surface-map.md` is the **accepted substitute** that satisfies the
51
+ §9.5 visual-map requirement — and you record the visual-coverage gap in the `_summary.md` `gaps:`
52
+ frontmatter (a known constraint, not a silent miss).
46
53
  - `_summary.md` — a short bridge file in the standard dump shape pointing into the above, so
47
- the synthesis step can treat session like any other dimension.
54
+ the synthesis step can treat session like any other dimension. Its frontmatter additionally carries
55
+ **`write_side_observed: <true|false>`** and **`pass2: <not-applicable | offered-pending |
56
+ declined-by-user | deferred | done>`** (§9.2) so Evaluation renders the write-side gap once,
57
+ machine-readably; a deferred Pass 2 may add an optional `captures/pass2-followup.md`.
48
58
 
49
59
  ## Return value
50
60
  A one-paragraph headline (the auth model + transport + the single most load-bearing wire
@@ -72,9 +72,11 @@ Then the captures + raw artifacts (per `re-discovery` Part C (wire-capture)):
72
72
  - `raw/flows.json` — verbatim-but-**redacted and digested** flows, each with the `_meta` header
73
73
  (incl. `kind`, `byte_length`, `redactions[]`, and the **chosen rung + authorization**), `body`,
74
74
  `_decoded`. Digest large captures (schema + one redacted sample + counts), never paste megabytes.
75
- - `raw/api-path-catalog.md` — the **shared-surface artifact** (§6): every row carries
76
- `source: wire`. Use this exact filename so evaluation can three-way diff it against `bundle` and
77
- `session` rows.
75
+ - `dimensions/_shared/api-path-catalog.md` — the **shared-surface artifact** (§6, one physical file):
76
+ **append** rows carrying `source: wire` to it (never a private copy), so evaluation can run its
77
+ adaptive diff against `bundle`/`session`/`distribution` rows. When `wire-capture` is folded into
78
+ `session` (the common path — §7.1), `session` owns this append; a standalone `wire-capture` appends
79
+ directly.
78
80
  - `curls.md` — reproducer `curl` skeletons with all credentials replaced by placeholders.
79
81
 
80
82
  ## Return value
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "re",
3
- "version": "0.1.2",
3
+ "version": "0.1.3",
4
4
  "epoch": null,
5
- "digest": "790ded87e841"
5
+ "digest": "8d5fbcaab511"
6
6
  }
@@ -146,6 +146,7 @@ The three named **cases** are presets over this vector — a target can light se
146
146
  (Liveblocks = source:full + runtime:reachable + auth:obtainable + presence:rich → nearly all
147
147
  dimensions). Rules:
148
148
  - **`auth:obtainable` is a hint, not actionable** — the harness never self-signs-up; it only counts as `have-login` if the *user* hands over a session.
149
+ - **A *claimed* login is `auth:claimed` (confidence medium), not `auth:have-login` (high), until a cheap live probe confirms it.** A user saying "I'm logged in" — or a session being merely *expected* — is not proof: profiles expire, a fresh tab may not restore an in-memory token, and a deep-link can bounce to a login wall while the root looks authed. Grade `auth:claimed` until Discovery (or the `session` agent's first step) navigates the app and confirms an **authenticated surface** (a dashboard, a populated `viewer`/`/me`, a token in the store) — only then promote to `have-login`. Grading a claimed login `high` lets a session collapse to `blocked` after dispatch, with the wasted dispatch hidden behind a confident grade.
149
150
  - **Dimension-level access overrides the target headline** for method selection (a Case-1 target can still have a Case-3 session dimension).
150
151
  - Method swaps on axis *value*: `source:full` ⇒ `clone-and-map`; `source:partial` ⇒ `source-map-reassembly`; `runtime:reachable + source:none` ⇒ `bundle-string-mine` + wire-capture.
151
152
 
@@ -172,6 +173,11 @@ Discovery records the resolved vector in the `access_grade` frontmatter of `00-r
172
173
  > **Deferred (available on request, not built):** `external-reputation` (G2/Reddit/HN — the only
173
174
  > "what others say" surface) and `hiring-intel` (job posts leak the backend stack). Strong
174
175
  > candidates, declined for this iteration. Re-add via self-correction if a run needs them.
176
+ > **Conditional trigger:** when `community` returns **no first-party feedback loop** (no public
177
+ > issues/roadmap/changelog/forum — feedback funnelled to a gated channel), flag `external-reputation`
178
+ > as **recommended-for-this-run** in `00-recon-plan.md`, so the user-sentiment blind spot is a surfaced
179
+ > choice rather than a silent gap (the `community` user-pain claims are then capped at tentative —
180
+ > re-evaluation closed-feedback cap).
175
181
 
176
182
  An **absent** dimension is a finding — record it with its evidence; never silently drop it.
177
183
 
@@ -183,6 +189,12 @@ See git history / the source playbook for the full strategy text; in brief:
183
189
  - **docs** — prefer in-repo docs, then `llms.txt`/`llms-full.txt`, then crawl; organize by section.
184
190
  - **packages** — per public package: registry metadata, `exports` map + `.d.ts` type surface, health.
185
191
  - **api** — fetch the spec verbatim, build an endpoint catalog (auth, envelope, pagination, webhooks).
192
+ **Grade the method from a RETRIEVED artifact, never a label.** Before grading `openapi-verbatim`,
193
+ actually fetch the candidate spec URL and confirm a **parseable JSON/YAML body** — a docs page merely
194
+ *titled* "OpenAPI" with no served `spec.json` (or a `/swagger` that 403s) is `docs-reconstructed`
195
+ (capped medium), and Discovery must pre-grade it that way so the plan matches reality. **A
196
+ sibling-declared host/op/route count is a coverage checklist (a floor), never a ceiling** — every
197
+ dumper expands from sibling-mined hosts/routes and records the predicted-vs-found delta.
186
198
  **NEW fold-in:** also probe MCP/tool surfaces — `/.well-known/mcp`, an MCP server manifest, a
187
199
  function-calling tool schema, an LLM "actions" file — and write `raw/tool-catalog.md` (an MCP tool
188
200
  list *is* an API catalog; it lives here, not in its own dimension).
@@ -190,7 +202,10 @@ See git history / the source playbook for the full strategy text; in brief:
190
202
  matrix, target market.
191
203
  - **community** — *first-party only*: changelog cadence + direction, issue/discussion themes, blog.
192
204
  - **session** — the deepest, opt-in, user-gated dimension; delegates to the session playbook in
193
- `re-ingestion` §9 (3-pass, P0–P6, captures/raw/curls).
205
+ `re-ingestion` §9 (3-pass, P0–P6, captures/raw/curls). For any `auth:have-login`/`auth:claimed`
206
+ target, Discovery **seeds the Pass-2 decision** (`pass2: offered-pending | declined-by-user |
207
+ deferred`) into `00-recon-plan.md`, so a Pass-1-only (read-only) session is a *recorded choice*, not a
208
+ silent omission of the write surface (re-ingestion §9.2).
194
209
 
195
210
  ## 8 — deployed-client-bundle — `deployed-client-bundle`
196
211
 
@@ -198,15 +213,22 @@ See git history / the source playbook for the full strategy text; in brief:
198
213
  (marketing) and `session` (authenticated). Works on closed SaaS with no repo and no login.
199
214
 
200
215
  **Detection:** an app subdomain (`app.<target>.*`) or SPA route serves a JS bundle;
201
- `sourceMappingURL` present; a `/assets/*.js` graph.
216
+ `sourceMappingURL` present; a `/assets/*.js` graph. **Two cheap Discovery probes pre-grade the method
217
+ and surface a leak early:** (a) does an entry chunk's `sourceMappingURL` resolve to a `.map`-200? →
218
+ pre-grade `source-map-reassembly` (confidence high — original `src/` recovers). (b) probe a few
219
+ bootstrap-config paths (`/config`, `/backend/config/*`, `/env.json`, a runtime-config endpoint) — a
220
+ 200 here is both a method pre-grade and a possible unauth config/secret leak (a finding).
202
221
 
203
222
  **Strategy:** fetch the main bundle(s); string-mine the **route table**, the **API-path catalog**
204
- (→ shared `raw/api-path-catalog.md`, `source: bundle`), **feature flags** (→ shared
205
- `raw/feature-flags.md`), embedded **env config** (`VITE_*`, `NEXT_PUBLIC_*`, public keys —
206
- field-names only, redact values), and reassemble **source maps** if shipped (`method:
207
- source-map-reassembly`, confidence high). Digest huge bundles, don't paste.
223
+ (→ append to the shared `dimensions/_shared/api-path-catalog.md`, `source: bundle` — re-ingestion §6),
224
+ **feature flags** (→ shared `dimensions/_shared/feature-flags.md`), embedded **env config** (`VITE_*`,
225
+ `NEXT_PUBLIC_*`, public keys — field-names only, redact values), and reassemble **source maps** if
226
+ shipped (`method: source-map-reassembly`, confidence high). Digest huge bundles, don't paste. This
227
+ dimension **owns** the app-API bundle mine — other dimensions cite its catalog by path, never re-mine
228
+ the same bundle (re-ingestion §6).
208
229
 
209
- **Artifacts:** `_summary.md` + `raw/{route-table.md, api-path-catalog.md, feature-flags.md, env-config.md, bundle-map.md}`.
230
+ **Artifacts:** `_summary.md` + `raw/{route-table.md, env-config.md, bundle-map.md}` + appends to the
231
+ shared `dimensions/_shared/{api-path-catalog.md, feature-flags.md}`.
210
232
 
211
233
  ## 9 — infra-backend-fingerprint — `infra-backend-fingerprint`
212
234
 
@@ -236,7 +258,17 @@ structured XHRs worth a tap, AND the appropriate ethics rung is authorized.
236
258
  2. HAR export → `wire-har`;
237
259
  3. mitmproxy (**gated, opt-in, user-run, first-party traffic only**) → `wire-mitmproxy`.
238
260
 
239
- **Artifacts:** `_summary.md` + `captures/wire-narrative.md` + `raw/{flows.json (redacted/digested), api-path-catalog.md (source: wire)}` + `curls.md`. Record the chosen rung + authorization in `_meta`.
261
+ **Fold note (common path):** when there is no separate non-browser client, wire-capture **folds into
262
+ `session`** (which owns the browser singleton), **never into a curl/WebFetch dumper** (a non-browser
263
+ dumper cannot run rung-1's in-page tap, and routing it there yields zero `source: wire` rows —
264
+ re-ingestion §7.1). The folded rung-1 *is* the interceptor `session` installs; its observed paths
265
+ append to the shared `dimensions/_shared/api-path-catalog.md` as `source: wire`. A folded wire-capture
266
+ still leaves a one-paragraph `_summary.md` (`status: folded`, naming the fold target) — never a silent
267
+ drop.
268
+
269
+ **Artifacts:** `_summary.md` + `captures/wire-narrative.md` + `raw/flows.json (redacted/digested)` +
270
+ appends to shared `dimensions/_shared/api-path-catalog.md (source: wire)` + `curls.md`. Record the
271
+ chosen rung + authorization in `_meta`.
240
272
 
241
273
  ## 11 — distribution-artifacts — `distribution-artifacts`
242
274
 
@@ -253,7 +285,7 @@ route/API/flag catalog from the desktop client (often richer than web). **mobile
253
285
  keys**). **CLI** — help text/subcommands = an API catalog. Read the artifact's EULA first (see
254
286
  `re-ingestion` §7.2); never silently decompile a restrictively-licensed binary.
255
287
 
256
- **Artifacts:** `_summary.md` + `raw/{extension-manifest.md, electron-asar-map.md, mobile-strings.md, cli-catalog.md}` (whichever apply) + `raw/api-path-catalog.md` (source: distribution).
288
+ **Artifacts:** `_summary.md` + `raw/{extension-manifest.md, electron-asar-map.md, mobile-strings.md, cli-catalog.md}` (whichever apply) + appends to shared `dimensions/_shared/api-path-catalog.md` (source: distribution).
257
289
 
258
290
  ---
259
291
 
@@ -23,6 +23,23 @@ coherent picture, **weighted by how each claim was obtained**, with disagreement
23
23
  `distribution-artifacts` are mutually independent and independent of marketing. **`website` and
24
24
  `docs` are NOT independent** for *feature claims* (docs often restate marketing) — two of them
25
25
  agreeing is still one source. A bundle-declared route + a session-observed route ARE independent.
26
+ **Same artifact ⇒ one source.** Two dimensions that string-mine the *same* static asset (one bundle /
27
+ one spec / one binary) are **one source** — never a band-bump — even when they report different
28
+ counts; the divergent counts are a **reported range, not a flagged conflict** (`deployed-client-bundle`
29
+ 278 vs `website` 361 over one bundle is one finding with a range, not corroboration and not a conflict).
30
+
31
+ **Closed-feedback confidence cap.** When the `community` dimension finds **no first-party feedback
32
+ loop** (no public issues / roadmap / changelog / forum — feedback funnelled to a gated channel), its
33
+ user-pain / most-requested-feature claims are **inference-from-changelog only → capped at
34
+ `low`/tentative**, never promoted on changelog frequency alone. The surface that *would* corroborate
35
+ user sentiment is `external-reputation` (a deferred dimension) — flag it as recommended-for-this-run
36
+ when this cap fires (see re-discovery Part C deferred-dimension trigger).
37
+
38
+ **Write-side-unobserved cap.** A `session` dimension carrying `write_side_observed: false` (a
39
+ Pass-1-only run) has observed feature *existence/gating* but **not** feature *execution behavior*. Any
40
+ marketed generate/submit/launch *behavior* claim therefore stays **tentative and is never promoted to
41
+ fact** from marketing/docs alone — render the write-side gap **once** from the frontmatter flag, not
42
+ re-hedged per rollup.
26
43
 
27
44
  **Presentation mapping:** high → stated as fact; low/single-source → hedged as tentative;
28
45
  conflicted → flagged. (This is the "lighter" form of the provenance-weighting — done inline in the
@@ -58,11 +75,19 @@ prose, not via a separate `claims.yaml`.)
58
75
  # <Target> — Data Model & API / SDK Surface
59
76
  ## Core entities (domain objects + relations; ER-style — when the runtime exposes an RBAC verb
60
77
  taxonomy, derive the entity set from it: `X_CREATE/VIEW/EDIT/DELETE` ⇒ entity X)
61
- ## Integration surface (SDK/packages public API · REST/HTTP catalog · realtime/wire protocol — flag
62
- where the app's OWN API differs from the published/developer API; that split is common)
78
+ ## Integration surface (SDK/packages public API · REST/HTTP catalog · realtime/wire protocol)
79
+ ## Published-vs-app-own API diff (emit WHENEVER the target publishes a developer/partner/MCP API —
80
+ common for mature SaaS, but not universal. The app the SPA runs on is frequently
81
+ a different, broader, differently-authed API than the published one: list ops the
82
+ app uses that the public API omits, AND public ops the app never calls — both are
83
+ findings. If there is NO published API, say so explicitly: "no published API — the
84
+ app surface is the only surface" is itself the finding, not an empty section.)
63
85
  ## Auth & tenancy model (token type, where it lives, scope expression)
64
- ## Three-way API-path diff (the seam rule: bundle-declared vs session-observed vs wire-observed —
65
- dormant/unshipped routes are findings)
86
+ ## API-path diff (adaptive, N-way over the runtime sources present) (the seam rule: bundle-declared vs
87
+ session-observed vs wire-observed. Title it for the sources ACTUALLY run —
88
+ enumerate only those lanes; an absent lane [session blocked / wire folded] is a
89
+ recorded gap, NOT a structural hole to apologize for. Dormant/unshipped routes
90
+ are findings.)
66
91
  ## Cross-dimension reconciliation (published surface [api/packages/docs] vs observed wire [session/
67
92
  wire-capture]; document drift — published=fact, observed-drift=tentative)
68
93
  ## Open questions
@@ -19,15 +19,28 @@ This contract generalizes the source playbook's three-format contract (`captures
19
19
  ## 1. Folder layout
20
20
 
21
21
  ```
22
- research/<target>/dimensions/<dimension>/
23
- ├── _summary.md ← decoded narrative + provenance frontmatter (human + machine read this)
24
- └── raw/ ← verbatim artifacts (the reproducer reads these)
22
+ research/<target>/dimensions/
23
+ ├── _shared/ ← cross-dimension seam artifacts, ONE physical copy (see §6)
24
+ │ ├── api-path-catalog.md
25
+ │ └── feature-flags.md
26
+ └── <dimension>/
27
+ ├── _summary.md ← decoded narrative + provenance frontmatter (human + machine read this)
28
+ └── raw/ ← verbatim artifacts, DIMENSION-PRIVATE (the reproducer reads these)
25
29
  ```
26
30
 
27
31
  Richer dimensions inherit the playbook contract (extra files, same `_summary.md` provenance):
28
32
  - **session**: `dimensions/session/{_summary.md, captures/p*.md, raw/*.json, curls.md}`
29
33
  - **wire-capture**: `dimensions/wire-capture/{_summary.md, captures/wire-narrative.md, raw/flows.json, curls.md}`
30
34
 
35
+ Two layout rules the seam and the "absence is a finding" principle depend on:
36
+ - **Shared-seam files live in `dimensions/_shared/`, one physical copy each — never under a single
37
+ dimension's `raw/`** (§6). Everything else under a dimension's `raw/` is **dimension-private by
38
+ default**; only the §6 allowlist (`api-path-catalog.md`, `feature-flags.md`) is shared.
39
+ - **A folded or absent dimension still leaves a record.** When a dimension is folded into another or
40
+ is absent, write a one-paragraph `_summary.md` with `status: folded` (naming the fold target) or
41
+ `status: absent` (+ the cheap evidence). "An absent dimension is a finding" must be **materialized
42
+ as a file**, never a silent drop.
43
+
31
44
  ---
32
45
 
33
46
  ## 2. `_summary.md` — required shape (with provenance frontmatter)
@@ -115,19 +128,34 @@ readable, not a memory dump. Set `digested: true` context in the artifact's `_me
115
128
 
116
129
  `deployed-client-bundle` (static unauth SPA), `session` (authenticated browser runtime), and
117
130
  `wire-capture` (non-browser client) can all surface the same **API path catalog** and **feature
118
- flags**. To keep them consumable instead of triplicated:
119
-
120
- - Use **one canonical artifact name** across all three: `raw/api-path-catalog.md` and
121
- `raw/feature-flags.md`, each row carrying a `source: bundle | session | wire` column.
122
- - Evaluation owns the **three-way diff** (static-declared vs browser-observed vs nonbrowser-observed)
123
- in `data-model-api-surface.md`. A path that appears in `bundle` but never in `session`/`wire` is a
124
- dormant/unshipped route — a finding, not noise.
131
+ flags**. To keep them consumable instead of triplicated **N times** (a real run forked
132
+ `api-path-catalog.md` up to 7×, and "two copies agree" then mis-reads as corroboration):
133
+
134
+ - **One physical file, one canonical location, append-only.** The seam files live at
135
+ `dimensions/_shared/api-path-catalog.md` and `dimensions/_shared/feature-flags.md` — **Ingestion
136
+ seeds the two empty files (with a header) once, before the dumper fan-out.** Every contributing
137
+ dimension **appends a `source:`-tagged section** (`source: bundle | session | wire | distribution`)
138
+ and **never creates its own copy**; each dumper's `_summary.md` Artifacts list **links** the shared
139
+ file rather than duplicating it. **Shared-seam allowlist:** these two filenames are the *only*
140
+ shared/single-physical artifacts — every other `raw/*` is dimension-private.
141
+ - Evaluation owns the **API-path diff** (an *adaptive N-way* diff over whichever runtime sources were
142
+ actually run — see re-evaluation template 3) in `data-model-api-surface.md`. A path that appears in
143
+ `bundle` but never in `session`/`wire` is a dormant/unshipped route — a finding, not noise.
144
+
145
+ > **Same artifact ⇒ ONE source (never independent corroboration).** Two dimensions that string-mine
146
+ > the *same* static asset (one bundle / one spec / one binary) are **one source** for the promotion
147
+ > rule — never a confidence band-bump — and their divergent counts are a **reported range, not a
148
+ > flagged conflict.** Name **one owner dimension per asset**: when `source:none` and no machine spec
149
+ > is served, `deployed-client-bundle` **owns** the app-API bundle mine, and `api` is re-scoped to the
150
+ > *published/partner* spec only (or marked `partial` if none is served) — the others **cite the owner
151
+ > by path and do not re-mine**.
125
152
 
126
153
  > **Published API ≠ the app's own API.** A product's developer/REST/MCP surface (`api`/`docs`/`packages`
127
154
  > dims) is frequently *not* the API its own app runs on (`session`/`bundle` dims) — different protocol,
128
155
  > host, and operation set. Always diff the two: operations the app uses but the public API omits, **and**
129
156
  > public operations the app never calls, are both findings. The published surface is the integration
130
- > story; the runtime surface is the real architecture.
157
+ > story; the runtime surface is the real architecture. When a published API exists, Evaluation emits
158
+ > this split as a *named* output, not inline prose (re-evaluation template 3).
131
159
 
132
160
  ---
133
161
 
@@ -163,6 +191,14 @@ chosen rung in `_meta`:
163
191
  *user* installs the CA and routes their client — the agent only ingests exported flows. →
164
192
  `method: wire-mitmproxy`. Same explicit-authorization loop as session Pass-2.
165
193
 
194
+ > **Never fold a browser-rung into a non-browser (curl/WebFetch) dumper.** Rung 1 *is* a browser tap,
195
+ > and the browser is a singleton owned by `session`/`wire-capture` — routing it to a curl dumper
196
+ > produces zero `source: wire` rows (a real run did exactly this). When `wire-capture` is **folded**,
197
+ > the rung-1 tap is the **in-page interceptor that `session` already installs**: `session` appends its
198
+ > observed paths to `dimensions/_shared/api-path-catalog.md` as `source: wire`. A folded `wire-capture`
199
+ > still leaves a one-paragraph `_summary.md` (`status: folded`, naming the fold target) per §1 —
200
+ > "folded" is recorded, never silently dropped.
201
+
166
202
  > Wireshark / packet capture is explicitly **not** used: for HTTPS it yields only TLS ciphertext,
167
203
  > and a packet sweep of a third party is the bright line this harness will not cross.
168
204
 
@@ -179,6 +215,15 @@ A dump is complete when: `_summary.md` exists with full provenance frontmatter +
179
215
  every Finding is backed by a `raw/` artifact or inline value; `raw/` artifacts have `_meta` +
180
216
  decoding (large ones digested); redaction pass run; `gaps` recorded (never fabricated).
181
217
 
218
+ **Summary-first + bounded, visible fan-out (never go silent long enough to look dead).** Write a
219
+ `status: partial` `_summary.md` with whatever is mined so far **before** any long-tail fetch loop
220
+ (downloading many chunks, crawling many pages, deep pagination), then enrich it in place and flip to
221
+ `complete`. **Bound** every fan-out — cap it by count / total bytes / wall-time (e.g. ≤40 items or
222
+ ≤90 s) and record the remainder as a `gap` rather than fetching exhaustively; emit a progress note per
223
+ batch. A dumper that fetches everything before writing anything can run many times longer than its
224
+ siblings and read as hung to the orchestrator, forcing a needless salvage. Partial-but-finalized beats
225
+ complete-but-silent.
226
+
182
227
  ---
183
228
 
184
229
  ## 9. The session dimension — logged-in browser-session playbook
@@ -225,6 +270,17 @@ completion event, server-side resolution of shorthand, ID/slug-generation, the r
225
270
  endpoint, and the full realtime event catalog. **Legitimate to skip** if the user opts out — leave
226
271
  write-side shapes as open questions; never fabricate them.
227
272
 
273
+ > **Record the write-side decision as a machine flag, not re-derived prose.** A Pass-1-only run leaves
274
+ > the *entire* write/generate/launch surface unobserved — feature *existence/gating* is solid, but
275
+ > feature *execution behavior* rests on marketing/docs and must never be promoted to fact (this caveat
276
+ > otherwise gets re-hedged across 3–4 rollups every cost-gated run). So the session `_summary.md`
277
+ > frontmatter carries `write_side_observed: <true|false>` and `pass2: <not-applicable | offered-pending
278
+ > | declined-by-user | deferred | done>` (this flag is the load-bearing part — it lets Evaluation render
279
+ > the write-side gap *once*). When it would help a later run resume, optionally add
280
+ > `captures/pass2-followup.md` listing the **exact cheapest-mode write actions + est. cost** that would
281
+ > close the gap. Discovery seeds the `pass2:` decision in `00-recon-plan.md` for any `auth:have-login`
282
+ > target.
283
+
228
284
  **Pass 3 — synthesis.** Walk the captures, pull each finding into the dimension's `_summary.md`,
229
285
  note the cleverest design choices, defaults imposed, performance characteristics, and open
230
286
  questions.
@@ -302,13 +358,21 @@ Under `research/<target>/dimensions/session/`:
302
358
  redacted) + `_decoded_fields`. Digest bodies > 50 KB into a structured summary.
303
359
  - **`curls.md`** — reproducible curl per endpoint, the auth model documented, a variables block,
304
360
  and an endpoint-catalog table.
305
- - **`captures/screens/`** (recommended) — one annotated screenshot per primary surface (each major nav
306
- section), plus a short tour recording if useful. The fastest "what does it actually do" artifact for a
307
- human reader, complementing the wire teardown — and the place the visual product map lives. Redact any
308
- visible PII before saving.
361
+ - **Visual / surface map** — `captures/screens/` (**preferred when available**): one annotated
362
+ screenshot per primary surface (each major nav section), redact visible PII before saving. **When
363
+ screenshot capture is unavailable** (e.g. the runtime lacks an image tool, or the app holds a
364
+ persistent connection that never reaches page-idle), a **textual `captures/surface-map.md` is the
365
+ accepted substitute** and satisfies this visual-map requirement — and the **visual-coverage gap is
366
+ recorded in the `gaps:` frontmatter** so Evaluation reads a *known constraint*, not a silent miss.
367
+ - **`captures/pass2-followup.md`** (optional, when Pass 2 is deferred/declined and it would help a
368
+ later run resume) — the exact cheapest-mode write/generate actions + estimated cost that would close
369
+ the write-side gap (§9.2). The `write_side_observed`/`pass2` frontmatter flags are mandatory; this
370
+ file is the convenience.
309
371
  - **`_summary.md`** — the standard dimension bridge file (provenance frontmatter per §2) pointing
310
372
  into the above, so Evaluation treats session like any other dimension. `method: session-playbook`,
311
- `confidence: low` for Pass-1-only/inferred.
373
+ `confidence: low` for Pass-1-only/inferred. **Session frontmatter additionally carries
374
+ `write_side_observed: <true|false>` and `pass2: <not-applicable | offered-pending | declined-by-user
375
+ | deferred | done>`** (§9.2) so Evaluation renders the write-side gap once, machine-readably.
312
376
 
313
377
  ### 9.6 Session ethics (binds hardest here — see §7 above)
314
378
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nurix/etna",
3
- "version": "0.15.5",
3
+ "version": "0.15.7",
4
4
  "description": "Bootstrap a project with a Nurix harness — copies the harness's CLAUDE.md, skills, agents, rules, and hooks directly into the project from a bundled payload (no marketplace fetch, no network).",
5
5
  "type": "module",
6
6
  "bin": {