@mstar-harness/dsh 3.10.3 → 3.11.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (30) hide show
  1. package/harness-commands/iteration-start.md +1 -1
  2. package/harness-skills/mstar-artifacts/references/plan-quality-bar.md +13 -0
  3. package/harness-skills/mstar-artifacts/references/status-and-residuals.md +1 -1
  4. package/harness-skills/mstar-audit/SKILL.md +2 -2
  5. package/harness-skills/mstar-audit/references/codebase-audit.md +53 -4
  6. package/harness-skills/mstar-audit/references/finding-format.md +44 -0
  7. package/harness-skills/mstar-audit/references/security-review.md +194 -32
  8. package/harness-skills/mstar-branch-worktree/SKILL.md +3 -2
  9. package/harness-skills/mstar-dispatch-gates/SKILL.md +7 -0
  10. package/harness-skills/mstar-host/references/cursor.md +1 -13
  11. package/harness-skills/mstar-host/references/omp.md +36 -5
  12. package/harness-skills/mstar-iteration/references/iteration-artifact-boundaries.md +7 -5
  13. package/harness-skills/mstar-iteration/references/iteration-compass-template.md +21 -3
  14. package/harness-skills/mstar-iteration/references/phase-1-prepare.md +37 -3
  15. package/harness-skills/mstar-iteration/references/phase-2-worktree-lease.md +3 -3
  16. package/harness-skills/mstar-iteration/references/phase-3-iteration-close.md +1 -0
  17. package/harness-skills/mstar-iteration/references/phase-6-post-merge-close.md +1 -1
  18. package/harness-skills/mstar-iteration/references/plan-scoped-pm.md +2 -0
  19. package/harness-skills/mstar-phase-gates/SKILL.md +1 -1
  20. package/harness-skills/mstar-roles/references/project-manager/dispatch-and-assignment.md +1 -2
  21. package/harness-skills/mstar-sdd/SKILL.md +1 -13
  22. package/harness-skills/mstar-sdd/references/file-handoffs.md +0 -1
  23. package/harness-skills/mstar-sdd/references/implementer-continuation-prompt.md +0 -1
  24. package/harness-skills/mstar-sdd/references/implementer-prompt.md +0 -1
  25. package/harness-skills/mstar-sdd/references/sticky-implementer-session.md +0 -1
  26. package/harness-skills/mstar-sdd/references/task-reviewer-prompt.md +0 -1
  27. package/harness-skills/mstar-use-cli/SKILL.md +1 -0
  28. package/harness-skills/mstar-use-cli/references/checks-and-lints.md +1 -1
  29. package/harness-skills/mstar-use-cli/references/plan-and-workflow.md +4 -0
  30. package/package.json +1 -1
@@ -15,10 +15,20 @@ Method behind the Security category. Load when the category focus is `security`,
15
15
 
16
16
  - Every security finding must state a concrete attack scenario: **who the attacker is, what they send or do, and what they gain** — "An unauthenticated caller sends `POST /api/orders` with `qty=0`, gets a negative-balance order."
17
17
  - "Potentially exploitable" / "theoretically" means the research is not done — name the actor, the request, and the effect, or downgrade the finding.
18
- - **Severity = likelihood × impact**, both judged from code evidence. A famous vulnerability class on an unreachable path is a hardening note, not a HIGH.
18
+ - **Severity is a likelihood × impact calibration, not a numeric multiplication** — judge each axis from code evidence, then place the finding on the anchor scale below; there is no formula to compute.
19
19
  - **Likelihood is judged from the repo's reality:** an endpoint behind a corporate VPN with no external callers is lower likelihood than the same shape on a public API; the code evidence stays the same, the rating does not.
20
20
  - **Impact is judged on the data, not the class:** SQL injection into a read-only lookup table is MEDIUM; the same class on a payment mutation is HIGH. Name what the attacker actually gains.
21
- - **HIGH vs MEDIUM discriminator:** the flaw defeats an explicit security boundary (authentication, authorization, tenant isolation, sandbox, trust boundary between components) → HIGH. It needs privileged access, a confined blast radius, or uncommon preconditions → MEDIUM.
21
+ - **Severity anchors — rank the confirmed effect, not the vulnerability class.** Rank order is `informational` < `low` < `medium` < `high` < `critical`:
22
+ - `informational` — a substantiated minimal-impact observation. **Informational anchors do not promote hardening notes or unverified security leads into findings** — those stay in the audit index (hardening rule above, §12 Needs verification).
23
+ - `low` — a demonstrated minimal gain or non-secret internal disclosure.
24
+ - `medium` — a demonstrated boundary violation with limited blast radius or uncommon preconditions.
25
+ - `high` — a demonstrated defeat of an explicit security boundary (authentication, authorization, tenant isolation, sandbox, inter-component trust boundary) **with substantial consequences** — a lower-trust principal performs an action it must not, and real damage follows. An explicit-control defeat without substantial consequences stays `medium`.
26
+ - `critical` — demonstrated unauthenticated code execution, full data-store access or arbitrary account takeover.
27
+ - **Anti-strengthening — never upgrade the effect class:**
28
+ - A crash or single-request denial is availability disruption, not code execution — do not rate it as execution-class.
29
+ - Ordinary load (large but bounded requests) is not shared-service harm; escalation requires cost imposed on other principals or shared infrastructure.
30
+ - Behavior affecting only the same principal (the user corrupts their own data, self-XSS, confusion over their own token) is not privilege escalation — an effect on another principal or protected shared state is required.
31
+ - **Scaffold field gate:** the finding scaffold enforces `severity.overall ≤ severity.impact` as a rank comparison (engine gate in `packages/engine/src/audit.ts`, violation `audit.finding.severity.overall-exceeds-impact`). It validates field consistency only — it does not verify that the impact is real; that stays reviewer judgment.
22
32
  - **A defense-in-depth gap where another layer already prevents exploitation is a Hardening note in the audit index's "Hardening & checked notes" section, not a findings row** — never severity-inflate it. Hardening notes get one index line and no plan unless the user asks.
23
33
  - **Confidence is per-claim, not per-category:** a repo with one sloppy auth check is not "insecure" — each row stands on its own evidence.
24
34
  - A finding needs both halves: the vulnerable pattern at `file:line` *and* a confirmed attacker-controlled input reaching it (§4). Either half unproven → keep researching (§3) or park it in the audit index's **Needs verification** section (§12).
@@ -82,6 +92,7 @@ Scan committed configs, CI workflows, Dockerfiles, and IaC for credential *patte
82
92
  - **Provider key shapes, never-commit file list, CI/IaC leak shapes, safe-placeholder exclusions — mechanical scan:**
83
93
  > **Engine check (when available):** run `mstar audit secret-scan [path]` (or `import { scanSecrets } from "@mstar-harness/engine"` in a host hook) to scan git-tracked files under a path for credential patterns — it prints `{file, line, type}` findings and exits 1 on any hit. The engine pattern tables (`WHOLE_MATCH_PATTERNS` / `VALUE_PATTERNS` / `NEVER_COMMIT_FILENAMES` / `CI_IAC_LEAK_SHAPES` in `packages/engine/src/audit.ts`) are the SSOT; do not re-enumerate patterns here. On `fail` -> do not proceed; fix and re-run. Skill text below remains authoritative when the runtime is absent.
84
94
  - **Entropy heuristic (reviewer judgment):** an assignment context (`=`, `:`, `KEY = value`) holding a 20+ character high-variety string — verify by context; entropy alone is noise.
95
+ - **Symbolic names are not secrets:** a public identifier named `key`, or a config field naming a secret reference (env-var name, secret-manager path, symbolic reference), is not a finding on its own — a finding requires credential authority (the name resolves to a live credential the holder can use) or actual value exposure to a lower-trust reader. Neither shown → Hardening/index note, not a finding.
85
96
  - Findings cite `file:line` + credential type only ("Stripe live key at `config.ts:12`"); the fix sketch always includes rotation, never just removal.
86
97
 
87
98
  ## 7. Cross-file data-flow sweep
@@ -97,21 +108,21 @@ Per-file scanning misses flows. After the per-file pass:
97
108
 
98
109
  Each angle is a reading lens, not a claim:
99
110
 
100
- - **Attack the sad path:** error, fallback, and retry branches skip validation — read the catch, the default case, the failure handler.
101
- - **Boundary values:** token expiry moment, exactly-at-limit sizes, multibyte vs byte limits, pagination edges.
102
- - **Implicit trust between components:** DB assumes API validated, worker assumes service A authorized, renderer assumes sanitize-on-write.
103
- - **Wrong order / replay:** flows that assume sequence — reuse-after-consume tokens, replayable webhooks, unbounded resend.
104
- - **Concurrency two-at-once:** double-spend, check-then-act, idempotency races on concurrent initialization.
105
- - **Parser disagreement:** router vs app normalization, extension vs MIME vs magic bytes, double URL-decoding.
106
- - **Trust in derived values:** cache keys built from user input, lookup tables keyed by attacker-chosen strings, IDs exposed in URLs that also gate authorization.
107
- - **Delegated checks:** validation that runs in the client, the test suite, or a sibling service but not on the production path — the enforcement point must be where the request lands.
108
- - **Round-trip survival:** stored → retrieved escaping drift that defeats earlier sanitization.
109
- - **Config posture:** missing config falling back to insecure defaults, env overriding a security control, first-run setup defaults, feature-flag defaults.
110
- - **Follow the money/privilege:** parallel paths to the same state change with weaker checks (alias routes, second entrypoints with fewer guards).
111
- - **Leaked context:** differential errors, timing, or response sizes → enumeration of users, resources, internal structure.
112
- - **Params overriding security-relevant defaults:** `debug=1`, `skip_auth`, `allow_*` knobs on request paths.
113
- - **Unhandled input shapes:** arrays where scalars are expected, extra keys in JSON bodies, oversized/malformed encodings reaching parsers that fail open.
114
- - **Unverified claims driving decisions:** client-set headers trusted server-side, `is_admin` hardcoded client-side, signature-verified but actor-unchecked tokens.
111
+ - **Attack the sad path:** error, fallback, and retry branches skip validation — read the catch, the default case, the failure handler. `Signal:` grep `catch` / `except` / `else` branches adjacent to `validate` / `verify` calls that return a default instead of rejecting.
112
+ - **Boundary values:** token expiry moment, exactly-at-limit sizes, multibyte vs byte limits, pagination edges. `Signal:` comparison operators at limits — `<=` vs `<` against `max*` / `limit` / `exp` / `length` / `count` identifiers.
113
+ - **Implicit trust between components:** DB assumes API validated, worker assumes service A authorized, renderer assumes sanitize-on-write. `Signal:` cross-module parameters or fields named `trusted*`, `verified*`, `sanitized*`, `checked*` consumed downstream without a re-check.
114
+ - **Wrong order / replay:** flows that assume sequence — reuse-after-consume tokens, replayable webhooks, unbounded resend. `Signal:` `consumed` / `used` / `redeemed` state columns or `eventId` / `idempotencyKey` dedup lookups that are read without an atomic claim (compare-and-set, unique constraint).
115
+ - **Concurrency two-at-once:** double-spend, check-then-act, idempotency races on concurrent initialization. `Signal:` a read (`SELECT`, `.get(`) of balance/quota/state followed by a later write with no transaction, lock, or version check in between; `getOrCreate` / upsert on init paths.
116
+ - **Parser disagreement:** router vs app normalization, extension vs MIME vs magic bytes, double URL-decoding. `Signal:` two decode/normalize calls over the same input — repeated `decodeURIComponent` / `urldecode`, `path` vs `url.parse().pathname` splits, `extname` vs `mimetype` vs content-sniff checks.
117
+ - **Trust in derived values:** cache keys built from user input, lookup tables keyed by attacker-chosen strings, IDs exposed in URLs that also gate authorization. `Signal:` template-literal key construction — `` `${ `` interpolations feeding `cacheKey` / `key` / `Map` / dict indexing from request fields.
118
+ - **Delegated checks:** validation that runs in the client, the test suite, or a sibling service but not on the production path — the enforcement point must be where the request lands. `Signal:` an authorization predicate (`is_admin`, `can_edit`, role checks) present in client bundles or `*.test.*` files with no corresponding server-side check at the handler.
119
+ - **Round-trip survival:** stored → retrieved escaping drift that defeats earlier sanitization. `Signal:` `escape` / `sanitize` / `encodeURIComponent` at the write path with a raw render read later at `innerHTML` / `v-html` / `dangerouslySetInnerHTML` sinks.
120
+ - **Config posture:** missing config falling back to insecure defaults, env overriding a security control, first-run setup defaults, feature-flag defaults. `Signal:` env reads with fallback defaults — `process.env` / `os.environ` followed by `||` / `or` / `??`, and flag names containing `DEBUG`, `SKIP`, `INSECURE`, `DISABLE`, defaulting truthy.
121
+ - **Follow the money/privilege:** parallel paths to the same state change with weaker checks (alias routes, second entrypoints with fewer guards). `Signal:` a second writer to the same target — another route/RPC/job issuing `UPDATE` / `.save(` / `.update(` on the same table or state the guarded path writes, with fewer middleware layers.
122
+ - **Leaked context:** differential errors, timing, or response sizes → enumeration of users, resources, internal structure. `Signal:` distinct outcomes for missing vs forbidden (`NotFound` vs `Unauthorized` branches), `err.message` interpolated into client responses, and secret comparisons using `===` / `==` instead of `timingSafeEqual` / constant-time compare.
123
+ - **Params overriding security-relevant defaults:** `debug=1`, `skip_auth`, `allow_*` knobs on request paths. `Signal:` request query/body keys matched against security-named flags — `debug`, `skip`, `bypass`, `allow`, `admin`, `impersonate` read on handler paths.
124
+ - **Unhandled input shapes:** arrays where scalars are expected, extra keys in JSON bodies, oversized/malformed encodings reaching parsers that fail open. `Signal:` `...body` / `...request.data` spread into model/ORM constructors, `JSON.parse` without schema validation, scalar-typed fields consumed in loops/queries without `Array.isArray` guards.
125
+ - **Unverified claims driving decisions:** client-set headers trusted server-side, `is_admin` hardcoded client-side, signature-verified but actor-unchecked tokens. `Signal:` `jwt.decode` (not `verify`) or a signature check whose payload fields (`sub`, `user_id`, `scope`) are then trusted without binding to the presenting principal.
115
126
 
116
127
  ## 9. Category expansions beyond playbook § 2
117
128
 
@@ -123,23 +134,38 @@ Apply where the repo actually has the surface. Absence is not a finding.
123
134
  - Password-reset tokens must be bound to the account, single-use, and expiring; token logged, unbound, or non-expiring is a finding.
124
135
  - Session fixation: no session rotation on privilege change (login, privilege escalation) — the pre-auth session survives privilege gain.
125
136
  - Session lifecycle: cookies without expiry or sliding refresh, sessions never invalidated server-side on logout, tokens valid after password change — stale credentials outlive the privilege change that should kill them.
137
+ - OAuth/OIDC callback binding: check each protection is present AND bound — `state` matches the initiating session, PKCE `verifier`/`challenge` pair, `nonce` inside the ID token, `redirect_uri` exact-match validated, and multi-IdP login picks the account the flow started for. A callback that accepts the code without tying it to the session that began the flow (login CSRF, code swap between accounts) is the finding; the protocol being present is not.
138
+ - SAML response binding: the identity consumed must be the verified object — the signed element (Response vs Assertion) is the one whose NameID/attributes are read, canonicalization is specified, validity window and audience/recipient (`Audience`, `Recipient`, `Destination`) match the service. Signature on one element while consuming the other is the finding; "SAML is complex" is not.
139
+ - MFA enrollment & assurance: enrollment or reset paths that a first-factor-only caller can reach, downgrade of a declared assurance level on sensitive actions, and step-up challenges not bound to the specific action being approved (a generic re-auth that approves a different, attacker-chosen operation). **Exclusion:** enrollment/recovery reachable only after a full first+second factor, or a step-up challenge bound to the exact approved action, refutes; MFA being optional for a low-risk tier is Hardening, not a finding.
140
+ - WebAuthn/passkey verification: challenge must be fresh and server-generated, RP ID / origin must match the relying party, credential ID must belong to the authenticating user, and `userHandle` must be checked on resident-key flows. Skipping any of these server-side is the finding; WebAuthn used on a plain password field adds nothing either way.
141
+ - Account linking & identity collision: linking flows keyed on email or a provider-supplied identifier that can match an existing account the caller does not own (pre-verify the identifier at each provider before auto-link); recovery paths that trust the same collision-prone match. **Exclusion:** a link that re-verifies ownership of the identifier at each provider (fresh challenge/pre-verify) before auto-linking, or keys on a provider-attested unique ID, refutes; an unmerged duplicate account is a UX bug, not a finding.
142
+ - Recovery surface breadth: audit every reset path, not just the public form — support/admin tools, backup codes, device/email/phone changes. Contact-info changes and code issuance that do not invalidate existing sessions/backup codes leave the attacker's foothold intact; that is the finding.
143
+ - API keys: key scope must bind to the resource set it is used against (a key scoped to project A reading project B is a finding); publishable-vs-secret distinction — a publishable key reaching a server-side-only context is noise, a secret key reaching a client bundle is a finding (§6 for values).
144
+ - mTLS & certificate lifecycle: the verified peer certificate must map to the application identity the request claims (a valid cert for any tenant is not tenant auth); revocation/expiry checks that fail open on a fetch error are the finding. "Uses OAuth/SAML/MFA" is never itself a finding, and a protocol mentioned in config without a reachable code path is not a surface. **Exclusion:** a documented fail-closed revocation/expiry path — or a cert-to-identity binding enforced at request time — refutes.
126
145
 
127
146
  ### Web protocol
128
147
 
129
148
  - Request smuggling needs TWO components disagreeing over bytes (edge proxy vs app, front server vs backend); a single-server repo has no surface — mark as a lead only when deployment adds a proxy/queue.
130
- - Host / `X-Forwarded-*` trust: password-reset links built from the `Host` header (host-header poisoning); `X-Forwarded-For` used for authz decisions without a trusted-proxy boundary.
131
- - Cache poisoning via unkeyed input: request headers that alter the response but are missing from the cache key.
132
- - Method/path normalization: routing that distinguishes `GET` vs `POST` where middleware runs on one method only; trailing-slash and case-insensitive duplicates of the same route with different checks.
149
+ - Host / `X-Forwarded-*` trust: password-reset links built from the `Host` header (host-header poisoning); `X-Forwarded-For` used for authz decisions without a trusted-proxy boundary. **Exclusion:** links generated from a fixed configured base URL, or the header used only for logging/rate-bucketing with no authorization effect, refutes.
150
+ - Cache poisoning via unkeyed input: request headers that alter the response but are missing from the cache key. **Exclusion:** a header that influences only per-request values (not the cached representation) — or that the cache itself keys on — refutes the concrete path.
151
+ - Method/path normalization: routing that distinguishes `GET` vs `POST` where middleware runs on one method only; trailing-slash and case-insensitive duplicates of the same route with different checks. **Exclusion:** route variants that all resolve to handlers with the same checks — or a normalizer that canonicalizes before middleware — refutes.
152
+ - Cache deception / private-response caching: an authenticated (or cookie-bearing) response stored by a shared cache under a path an attacker can make the victim request — look for cache keys that strip query strings/extension normalization (`.js`/`.png` suffix tricks) and `Cache-Control` on authed responses. Not a finding when the authed response is explicitly non-cacheable, the path is not attacker-choosable, or an effective upstream control separates private from cacheable responses.
153
+ - Response-header injection: attacker-controlled values flowing into header-setting calls (`Set-Cookie`, `Location`, custom headers) carrying CR/LF or other control characters. A framework that strips/rejects control characters in header values refutes the concrete path.
154
+ - CSRF inventory breadth: cover every cookie-authenticated mutation — legacy endpoints predating the CSRF layer, `method-override` parameters that swap methods around middleware, login CSRF (attacker logs the victim into the attacker's account). A safely rejected request (origin check, token bound to session) or an effective upstream control refutes the concrete path; absence of a token on a same-site-only, non-mutating route is not a finding.
133
155
 
134
156
  ### Business logic & abuse
135
157
 
136
- - Workflow state-machine bypass: skip, go backwards, or replay completed steps; check the flow state, not just entry validation.
137
- - Price/discount client-trust: price math, coupons, or quotes computed client-side and trusted server-side.
138
- - Export / import / search as exfil-oracle: unbounded export scopes, cross-tenant export filters, search as enumeration.
139
- - Enumeration via side effects: signup/login/reset responses that leak account existence through timing or message differences.
158
+ - Workflow state-machine bypass: skip, go backwards, or replay completed steps; check the flow state, not just entry validation. **Exclusion:** a transition guard on every entry point to the state (not just the happy-path route) — or a transition with no privilege/value consequence — refutes.
159
+ - Price/discount client-trust: price math, coupons, or quotes computed client-side and trusted server-side. **Exclusion:** the server recomputing price from its own records at settlement refutes; a client-supplied value used only for display is noise.
160
+ - Export / import / search as exfil-oracle: unbounded export scopes, cross-tenant export filters, search as enumeration. **Exclusion:** export/search scoped and filtered to the requesting principal's own records refutes.
161
+ - Enumeration via side effects: signup/login/reset responses that leak account existence through timing or message differences. **Exclusion:** uniform responses and work-equivalent timing across existing/non-existing accounts — or a public, by-design directory (the account list is the product) — is not a finding.
140
162
  - Missing rate limits on auth/reset/expensive endpoints — respecting the deployment model: a CDN-layer or API-gateway rate limit is valid architecture, do not flag its absence at the service layer when it exists elsewhere.
141
- - **Idempotency and replay:** retried webhooks, replayed requests, and double-submission on payment/order paths — check the idempotency key is bound to the actor, not just present.
142
- - **Mass action surfaces:** bulk update/delete/export endpoints that skip the per-item checks single-item endpoints enforce.
163
+ - **Idempotency and replay:** retried webhooks, replayed requests, and double-submission on payment/order paths — check the idempotency key is bound to the actor, not just present. **Exclusion:** a key bound to the actor and atomically claimed (compare-and-set/unique constraint) refutes; a present-but-unbound key is the finding, not a mitigated one.
164
+ - **Mass action surfaces:** bulk update/delete/export endpoints that skip the per-item checks single-item endpoints enforce. **Exclusion:** a bulk endpoint applying the same per-item check to every item refutes.
165
+ - Numeric manipulation: sign flips, zero/negative quantities, integer overflow, floating-point precision, string-to-number coercion on price/quantity/credit paths — reachable only when the computed value is trusted downstream (balance, limit, entitlement). Unusual arithmetic on a display-only value is not a finding.
166
+ - Partial-failure rollback: multi-step writes (payment + order, transfer legs, quota + record) where one side commits and the other fails or is retried — the surviving half must not confer value. A transaction boundary or compensating action on the failure path refutes it.
167
+ - Time boundaries: expiry comparisons (`<` vs `<=`), clock-skew tolerance windows, timezone-dependent "end of day" cutoffs, backdated/future timestamps accepted from input where the server's clock is the intended authority. The effect must be an unauthorized state reached through the time gap, not a stylistic preference.
168
+ - Default & fallback posture: missing config, disabled flags, or dependency outages that fall back to allow/open/zero-cost instead of deny; migration-era code paths still reachable. The failure branch must be reachable and grant something real; a closed fallback is not a finding. **Exclusion:** require reachable unauthorized state or financial effect — an odd-but-bounded computation with no trust consequence is noise.
143
169
 
144
170
  ### Client-side
145
171
 
@@ -150,6 +176,12 @@ Apply where the repo actually has the surface. Absence is not a finding.
150
176
  - CORS: reflected origin with `Access-Control-Allow-Credentials: true` → flag; a bare `*` wildcard without credentials is not a finding.
151
177
  - Client-stored state: tokens in `localStorage` are a note in most apps (XSS is the real boundary); flag only when a CSRF-exposed or multi-origin surface makes them reachable.
152
178
  - History and referrer: sensitive identifiers in URLs leak through `Referer` to third parties; flag when the identifiers gate access.
179
+ - DOM clobbering: attacker-retained markup (an injection sink that persists in the page) whose named elements/ids collide with script-referenced globals or `document.*` lookups — requires BOTH the retained markup AND a security-relevant consumer (a config value, permission flag, or sink argument read through the clobbered name). Clobbered names with no consumer are not a finding.
180
+ - Cross-site WebSocket: `new WebSocket(url)` on a cookie-authenticated endpoint without origin validation on the handshake — the browser sends cookies cross-site. A server that validates `Origin` (or the app is not cookie-authenticated) refutes it.
181
+ - Service workers: registration scope wider than the pages it intercepts, and cache-identity confusion — a worker serving cached responses keyed without URL/version/tenant discrimination can serve one user's or one version's response to another. Not a finding when scope is narrowly registered and cache keys are complete.
182
+ - Cross-context storage: `localStorage`/`sessionStorage`/`BroadcastChannel`/shared workers reachable from other same-origin contexts (other apps on the same origin, stale tabs continuing to act after logout). Flag when a lower-privilege same-origin app reads another's data or a stale tab retains authorization; same-app session restoration is not.
183
+ - `window.name` and URL fragments: values carried across navigations/origins (`window.name`, `#fragment`) consumed as configuration, redirect targets, or rendered content. Consumption as page data with the page's own trust is not a finding; crossing a trust boundary into a sink is.
184
+ - XS-Leaks: requires a concrete secret-bearing predicate a cross-site principal can evaluate — an error/size/status/timing difference that answers yes/no about another user's data (e.g. search results distinguishing "exists", cross-origin frame counting a protected page). Generic timing variance or response-time jitter without the predicate is not a finding.
153
185
 
154
186
  ### AI/LLM features
155
187
 
@@ -163,16 +195,37 @@ Apply where the repo actually has the surface. Absence is not a finding.
163
195
  - Guardrail prompts are not security controls; the enforcement boundary is the handler, not the system prompt.
164
196
  - Model-scope escalation: a model that can read more than its user (shared tool session, service-account context) turns any prompt into a privilege edge — name the capability the user lacks.
165
197
  - Streaming and caching: LLM responses cached or logged without redaction can persist PII beyond the request lifecycle; check the cache key and retention like any other store.
198
+ - Persistent memory poisoning: injected content written to durable memory (saved notes, learned preferences, stored summaries) only counts when a reachable consumer later acts on it — name the reader and the action it drives. Memory nothing reads back is not a finding.
199
+ - Role / provenance confusion: content from a lower-trust source rendered or treated as system/developer role (tool output formatted as system messages, retrieved docs injected above user turns). Name the lower-trust author and the privilege the role label grants it. **Exclusion:** content kept in its declared role (tool/user) with no privilege attached to the label refutes.
200
+ - Action & approval binding: confirmations must bind to the specific action payload — a "yes" approving a different tool call than the one shown, or an approval captured before the action is finalized, is the finding. **Exclusion:** an approval bound to the exact finalized payload and captured after finalization refutes; the user approving the action actually shown is not a defect.
201
+ - Tool-schema vs dispatcher disagreement: the declared tool schema (params, constraints) differing from what the dispatcher actually passes/executes — the dispatcher's path is the real attack surface; a correct schema with a diverging executor is the finding. **Exclusion:** a dispatcher that executes exactly the declared schema — or a divergence with no capability difference — refutes.
202
+ - Sub-agent & MCP trust inheritance: a sub-agent or MCP server inheriting the parent's authority (service identity, tenant scope, credentials) without its own checks — name the execution principal and the capability it gains beyond its caller's intent. **Exclusion:** a sub-agent running under a scoped principal whose capabilities match its caller's stated intent, re-checking its own inputs, refutes.
203
+ - Peer identity & metadata-as-policy: peer-supplied identity claims or metadata (agent names, labels, capability announcements) treated as authorization decisions. Metadata describes; it does not authorize — a policy decision made from attacker-writable metadata is the finding. **Exclusion:** metadata used only for display/routing while authorization comes from a verified credential refutes.
204
+ - Cross-session / cross-tenant context bleed: conversation history, cached context, or workspace state shared across sessions or tenants — the query/retrieval boundary must enforce isolation (§9h applies to the store). **Exclusion:** a retrieval query that enforces session/tenant isolation as a filter (not metadata labels or client-supplied IDs) refutes.
205
+ - **Exclusion:** for every fold above, name the lower-trust author, the execution principal, and the capability gained. "Prompt injection is possible" alone remains insufficient — the existing first bullet governs.
166
206
 
167
207
  ### Supply chain & CI/CD
168
208
 
169
- - Exactly one authoritative lockfile at the install boundary: missing, gitignored, or bypassed lockfile is a reproducibility + supply-chain finding.
209
+ - Exactly one authoritative lockfile at the install boundary: missing, gitignored, or bypassed lockfile is a reproducibility + supply-chain hygiene finding — but reporting it as exploitable still requires naming a reachable trust/authority failure (closing exclusion below): reproducibility alone is not authenticity.
170
210
  - Unreviewed dependency lifecycle scripts: install/postinstall scripts from new or low-signal dependencies.
171
211
  - Typosquat signals: near-squat names, freshly-published packages, zero-download "familiar" packages.
172
212
  - Unpinned CI actions (`@main` / `@latest`) and `pull_request_target` that checks out the PR head — the two together execute untrusted code with privileged secrets.
173
213
  - Never recommend forced remediation (`audit fix --force`, `npm audit fix --force`) — it bumps majors without review.
174
214
  - Registry scope: private registries used for public packages, registry mixing in one manifest, and packages pulled from unauthenticated mirrors.
175
215
  - Publish provenance: npm/GitHub provenance attestations absent on release-critical packages is a note, not a finding, unless the supply chain is the repo's product.
216
+ - CI configuration is authorization code: read workflow files as policy — what untrusted input (`pull_request_target`, `issue_comment`, forks) can trigger, what secrets/capabilities/checkouts each trigger reaches. Compare untrusted-event triggers against protected-event triggers; a privileged step reachable from an untrusted trigger is the finding. **Exclusion:** an untrusted trigger reaching only unprivileged steps (no secrets, read-only or no checkout) refutes.
217
+ - Cache/artifact/workspace trust mixing: caches or artifacts written by untrusted runs restored into privileged ones (`actions/cache` keyed on branch an attacker can push to, artifacts downloaded without run-source checks, workspace state persisting across jobs with different trust). **Exclusion:** caches keyed on immutable refs/content hashes, or artifacts consumed only after run-source verification, refutes.
218
+ - Expression/command confusion: `workflow_run`/event JSON fields (`title`, `branch`, `head_commit.message`) interpolated into `run:` shell or script contexts; matrix values derived from untrusted event payloads. `${{ }}` in a `run:` block is untrusted input at a shell sink. **Exclusion:** untrusted fields interpolated only into non-executing contexts (job/step names, labels) or passed as arguments to a step that does not eval them refutes.
219
+ - Build-context inclusion: secrets or env needed by one build stage placed in a context (Docker build context, artifact upload scope) readable by a wider stage/consumer than intended. **Exclusion:** secrets scoped to the exact consuming stage (BuildKit secrets, per-stage mounts, scoped uploads) refutes.
220
+ - Promotion digest binding: the digest attested/built must equal the digest promoted/deployed — promotion flows that re-resolve a tag instead of pinning the built digest lose the binding. **Exclusion:** promotion that pins and re-verifies the built digest refutes.
221
+ - Attestation claims: signature/attestation verification must check identity claims (subject, repo, workflow, issuer), not just that a signature exists; verifying an attacker-attested artifact is not verification. **Exclusion:** verification matching subject/repo/workflow/issuer against the promotion policy refutes.
222
+ - Fail-closed lifecycle: expiry, revocation, and rotation of tokens/signing keys that fail open on fetch/refresh errors; an updater that accepts metadata when the timestamp/rotation check errors is the finding. **Exclusion:** a documented fail-closed path — reject or hold on fetch/refresh error — refutes.
223
+ - Updater integrity: update metadata verification (signer, version monotonicity, rollback acceptance) and atomic install — a partially applied update executable on the failure path is the finding. **Exclusion:** an updater verifying the signer, rejecting non-monotonic versions, and installing atomically (staged swap) refutes.
224
+ - Extension/plugin hooks: hooks or plugin entry points that run before trust checks (pre-install scripts, extension init with host credentials) — order of execution vs order of validation is the check. **Exclusion:** hooks running after trust checks, or under an unprivileged identity, refutes.
225
+ - Error-path policy: failure branches of CI steps and deploy scripts that retry open, skip verification, or dump secrets into logs/artifacts on error. **Exclusion:** failure branches that fail closed and redact secrets refutes.
226
+ - Source/namespace confusion: dependency resolved from a different registry/source/namespace than the manifest declares (scope squatting on internal namespaces, mirror override, `--registry` on the CLI but not in the lock). **Exclusion:** a lockfile/resolution pinning the declared source, with no registry override reachable on the install path, refutes.
227
+ - Mutable build inputs: floating refs (`@main`, `latest`, branch pins) in actions, base images, or dependency specs feeding privileged builds. **Exclusion:** a mutable dependency alone is not a finding — the float must feed a privileged build or a shipped artifact.
228
+ - **Exclusion:** reproducibility is not authenticity — a reproducible build of attacker-chosen inputs proves nothing. Dependency mutability, a CVE, or missing least privilege alone is not proof of a reachable exploit; name the trust/authority failure an attacker reaches.
176
229
 
177
230
  ### Infra configs
178
231
 
@@ -181,14 +234,102 @@ Apply where the repo actually has the surface. Absence is not a finding.
181
234
  - Debug modes and default credentials in production config: actuator/debug endpoints exposed, default admin passwords, verbose stack traces.
182
235
  - Network exposure: services binding `0.0.0.0` without a stated reason, admin/management ports on public interfaces, health or metrics endpoints answering unauthenticated requests with internal state.
183
236
  - Backend service config: database connections over plaintext, missing auth on internal caches/queues (Redis, RabbitMQ), and service-to-service credentials embedded in source.
184
-
185
- ### Privacy / retention
237
+ - Workload identity / IAM: role or service-account permissions judged against what the workload actually calls, not the class "too broad"; an unused permission without a reachable abuse path is a Hardening note.
238
+ - Cross-account role binding: assume-role/impersonation paths must bind external ID / audience / tenant condition — a role assumable by any principal in the trusted account is the finding. **Exclusion:** a checked-in trust policy requiring the external ID/audience/tenant condition (and the caller's config showing it is passed) refutes.
239
+ - Application trust in metadata: platform-injected headers/labels/annotations (mesh headers, task metadata, k8s labels) consumed for authorization inside the application — the platform attests placement, not permission; an app-level decision made from them is the finding. **Exclusion:** metadata used only for display/routing while the authorization decision comes from a verified credential refutes.
240
+ - Mesh/proxy escapes: alternate ports bypassing the sidecar, health/readiness paths exempt from auth but serving state, fail-open modes on the mesh or proxy config. **Exclusion:** the alternate path enforcing the same auth chain as the primary (same interception, authenticated health path, fail-closed mesh config) refutes.
241
+ - Metadata-service reachability: workloads that can reach the cloud metadata service (IMDS) with credentials-returning routes, and IMDSv2-style mitigations absent where the runtime supports them. **Exclusion:** checked-in config showing the mitigation enforced (token-required IMDS option, hop limit, or a network policy blocking IMDS) refutes.
242
+ - Admission & restore/upgrade paths: policy that enforces at admission but not on restore-from-backup, upgrade, or direct-API mutation of the same objects — the object can enter through a path the policy does not gate. **Exclusion:** the same policy demonstrably enforced on every mutation path (a controller reconciling all entry paths, or admission coverage of restore/upgrade) refutes.
243
+ - Namespace/label trust: isolation or authorization derived from namespace names or labels an unprivileged principal can create or relabel. **Exclusion:** authorization bound to verified credentials rather than namespace/label names — or a policy restricting those labels to principals the policy already trusts — refutes.
244
+ - Security-control precedence: two controls of different strength covering the same path (a restrictive NetworkPolicy plus an allow-all default, an RBAC deny plus a wildcard role) — the effective weakest control governs; name which one the request actually meets. **Exclusion:** the weaker control not being on the request's actual path (the allow-all default unreachable for these routes, the wildcard role ungrantable to this principal) refutes.
245
+ - Credential renewal & outage fallback: token/secret renewal paths that on outage fall back to the previous (or unauthenticated) credential instead of failing closed. **Exclusion:** checked-in config/code showing a fail-closed fallback — reject or queue on renewal failure — refutes.
246
+ - Signed references & object policies: signed URLs / pre-signed objects scoped wider than the feature that issues them (wildcard resources, long TTLs, no method restriction); object/store ACLs disagreeing with the application's tenant model. **Exclusion:** signatures scoped to exactly the issuing feature's resources with restricted methods and TTLs, and store ACLs matching the tenant model, refutes.
247
+ - Event-source identity: consumers trusting event payloads' claimed identity without verifying the source binding; replay of already-processed events; dead-letter queues readable or retained beyond the source's trust scope. **Exclusion:** consumers verifying a source binding (signature/envelope identity) before acting, replay dedup, and DLQ ACLs scoped to the source's trust refutes.
248
+ - Edge vs origin mismatch: CDN/edge runtime behavior (rewrites, header normalization, auth at edge) that the origin does not assume — origin trusting an edge-only header, or edge passing paths the origin router normalizes differently. **Exclusion:** the origin deriving decisions only from values it independently verifies — or edge and origin config showing identical normalization — refutes.
249
+ - **Evidence discipline:** inspect already-available effective configs/manifests and source precedence (repo files, rendered artifacts checked in, `kubectl get -o yaml` output committed into the repo); **never run** deployment/render/build commands during an audit. Manifest-only assumptions or unknown provider defaults remain **requires runtime verification** (§12), not lower-severity confirmed findings.
250
+
251
+ ### Data isolation & lifecycle
186
252
 
187
253
  - PII classification: name the fields that are PII here (identity, credentials, money, contact, content) before assessing.
188
- - Retention: personal-data stores need a TTL and a working deletion path — backups, caches, and indexes included; a deletion function that misses any of these is a finding.
189
254
  - Sensitive fields in API responses or logs: tokens, money fields, PII in debug output, structured logs without redaction.
190
- - Deletion-path verification: an API that deletes the record but leaves the file, the blob, or the analytics event is a retention finding even when the primary store is clean.
191
255
  - Export surfaces: bulk export, backup, and data-portability endpoints that return more than the requesting tenant owns are both a privacy and an IDOR risk.
256
+ - Record lineage: trace one record's copies across write/query/cache/index/event/export/backup/delete/restore — every hop must carry the original's access boundary. A hop that drops or widens it (cache keyed without tenant, index without ACL filter, export without scope) is the finding.
257
+ - Composite-key & namespace collision: keys/namespaces combining tenant + resource where attacker-chosen components can collide across tenants (string concatenation without separator or escaping, shared global sequences) — the collision must let a lower-trust principal read or write the other tenant's record to count.
258
+ - Policy vs query disagreement: the declared isolation policy (RLS, row filter, tenant middleware) differing from what the actual query path enforces — a raw-query or admin-code path skipping the tenant filter is the finding even when the standard ORM path is clean.
259
+ - Blob & signed-reference scope: stored objects reachable through references (signed URLs, IDs, paths) granting wider access than the owning record's ACL; derived copies (thumbnails, exports, embeddings) inheriting store defaults instead of the source's ACL.
260
+ - Import/restore authority expansion: import or restore flows executing with more authority than the calling user (system identity, cross-tenant targets, unvalidated ownership on the imported records) — the imported data must land inside the caller's boundary.
261
+ - Migration & backfill ownership: migrations/backfills/rollbacks that read or write across tenant boundaries, or set ownership/tenant fields by assumption rather than source-of-truth lookup; rollback code is code — audit it like a write path.
262
+ - Backup/replication drift: replicas or backups with a different access boundary than the primary (broader readers, weaker auth, longer retention) — data whose protection ends where the replica begins is a boundary breach, not a lifecycle preference.
263
+ - Tombstones & re-registration: soft-deleted records resurrected by ID reuse or re-registration, or visible through views/queries that don't filter the tombstone; deletion must survive the identifier's reuse.
264
+ - Stale authorization beyond sessions: authorization state cached outside the session — grants materialized in tokens, group membership in long-lived jobs, capability URLs issued before a revocation that never reach them. Revocation must propagate to every consumer of the grant, or the finding stands.
265
+ - Retention & deletion guarantees: missing TTL alone is **not** a finding — it is a Hardening note. A retention finding requires an explicit access/deletion/revocation guarantee the code claims plus a demonstrated breach: an unauthorized reader, or a subsequent operation the guarantee said was impossible. Incomplete deletion (record deleted, blob/cache/index/analytics copy left) remains a finding when it demonstrably breaches the declared boundary — which copy, which reader.
266
+
267
+ ### 9i. Desktop, mobile & local IPC
268
+
269
+ Reportable path requires a **lower-trust caller, a privileged consumer, and an unauthorized effect** — all three. Same-user arbitrary plugin/app installation is not a boundary (any user can run code as themselves); look for a distinct protected principal or a capability boundary the caller should not cross.
270
+
271
+ **Webview & renderer surfaces**
272
+
273
+ - Webview navigation & privileged bridges: a webview granted bridge/native-API access (`addJavascriptInterface`, `WKScriptMessageHandler`, Electron `ipcRenderer` in the renderer) that can be steered by navigation to attacker-influenced content — the bridge must be gated on a verified, first-party origin/page, not on "the webview exists". **Exclusion:** bridge messages validated against an origin allowlist checked at message time, or the bridge exposing no capability beyond what the page already has, refutes.
274
+ - Renderer/process isolation: privileged work (file access, token handling) executed in the same process/trust domain as untrusted rendered content — name the capability the renderer process gains. Site-process isolation or contextIsolation enabled with no node/IPC escape refutes.
275
+
276
+ **Entry & address handling**
277
+
278
+ - Deep-link ownership & account binding: a custom-scheme/universal-link handler that accepts an action (login confirm, token consumption, account switch) without binding the link to the expecting account/session — an attacker-sent link acting on the victim's account is the finding. **Exclusion:** the handler re-verifying the acting account against the link's target before executing refutes.
279
+ - URI normalization: scheme/host/path parsed differently by the entry handler and the privileged consumer (scheme confusion on `intent://`-style URIs, case/percent-encoding surviving into an allowlist check). The effect must be reaching a consumer the declared scheme excludes; a canonicalizing parser refutes.
280
+
281
+ **Local IPC channels**
282
+
283
+ - Named pipe / socket access controls: a locally exposed pipe/socket performing privileged work with peer checks absent or checkable by connection alone (filesystem permissions on the socket path as the only gate, world-accessible service sockets). **Exclusion:** the server verifying peer credentials/uid at accept time and binding them to the request's authority refutes.
284
+ - Binder/D-Bus/XPC interfaces: exported/registered IPC methods whose validators check the *caller-claimed* parameter but not the *caller* — privileged method reachable from any app/session. **Exclusion:** per-caller policy (`XPCConnection` audit, Binder `checkCallingPermission`/uid checks, D-Bus policy) enforced on the interface refutes.
285
+ - IPC peer credentials vs payload identity: the peer's OS-level credential (uid, pid, connection attestation) is the only caller identity; identity *claimed in the payload* (a username, tenant ID, "admin" flag field) is an assertion, not authentication. A privileged consumer trusting the payload's claim while the peer credential belongs to a lower-trust sender is the finding. **Exclusion:** the consumer comparing the verified peer credential against the payload claim and rejecting mismatch refutes.
286
+
287
+ **Secrets, files & installation**
288
+
289
+ - Keychain/keystore access groups: credentials stored under an access group/keychain-sharing entitlement readable by other apps/binaries of the same team signing different products — name the second consumer. Distinct access groups or single-consumer storage refutes.
290
+ - Shared-file & temp-path ownership: privileged code reading/writing a predictable world-writable or group-writable path (temp file symlink swap, cache poisoning feeding a privileged read). **Exclusion:** `O_CREAT|O_EXCL`/`O_NOFOLLOW`-style creation or a user-private directory refutes.
291
+ - Installer/updater authority: an installer, updater, or privileged helper executing payloads (scripts, binaries, package contents) whose placement a lower-trust writer can influence (writable staging dir, unsigned payload, mutable download verified only at first run). Unknown or indeterminate signing or device policy is **requires runtime verification** — a **Needs verification** lead (§12). **Exclusion:** signature verification at execution time by the privileged side, over a path the untrusted writer cannot write, refutes.
292
+ - Merged manifest/entitlement overrides: the effective policy is the merge of platform manifest, build config, and overlay files — an override layer (build type, product flavor, debug overlay) adding a permission, exported component, or entitlement the source declaration does not show. Inspect the merged effective artifact in the repo; a merge you cannot determine from checked-in files is **requires runtime verification** — a **Needs verification** lead (§12). **Exclusion:** an inspectable effective artifact (merged manifest/entitlement output) showing no permission, exported component, or entitlement beyond the source declaration — no unauthorized override — refutes.
293
+ - Extension permissions: a browser/editor/agent extension granted host permissions whose content or background code can act with the host app's authority beyond the permission's stated scope (all-URLs permission plus message-passing bridge to any page). **Exclusion:** the extension's privileged paths verifying message origin/sender against the declared scope refutes.
294
+
295
+ **Cross-app actions**
296
+
297
+ - Clipboard consumers: privileged features auto-consuming clipboard contents (token/OTP paste, "open link from clipboard") where any lower-trust app controls the clipboard. The check is the privileged action on attacker-chosen content; clipboard access being common is not itself a finding.
298
+ - Intent forwarding/redirection: an exported component forwarding attacker-supplied intents/bundles to privileged internals (intent redirection, nested `Intent` extras executed with the app's identity). **Exclusion:** the forwarding component re-granting only its own (unprivileged) permissions or validating the target refutes.
299
+ - Exported background services: exported services/receivers performing sensitive work (sync, backup, account ops) invocable by another app with attacker-chosen parameters. **Exclusion:** permission-protected exports, or parameter validation binding the action to the caller's own account, refutes.
300
+ - Notification actions: action buttons/quick replies executed with app privileges but content influenceable by another app's notifications (action routed by notification extras an attacker's notification also carries). **Exclusion:** the handler verifying the notification's identity/ownership before executing its action refutes.
301
+
302
+ ### 9j. Memory safety & binary
303
+
304
+ Source review only — no sanitizer/fuzzer execution steps. Trace untrusted data from the parser/FFI boundary through **size/unit conversion → allocation → ownership transfer → alias use → release**; a finding names the broken step and the supported effect.
305
+
306
+ - Integer boundaries: subtraction underflow (length minus length), size multiplication without overflow check, narrowing conversions (`size_t` → `u32`), negative-to-unsigned casts, and sentinel values (`-1` as length/count) flowing into allocation or copy sizes. The converted value must reach an allocation/copy/index to count; a rejected or clamped path refutes.
307
+ - Ownership & lifetime: stale aliases after transfer (use-after-free), double-free, observers draining a collection while a worker iterates, reference-count manipulation on shared objects across threads (racy `retain`/`release`), and TOCTOU where the file/handle is re-validated then reopened. Name the second use or second release; a single-owner discipline refutes.
308
+ - ABI & layout: cross-language structs/enums whose layout, size, or discriminants disagree across the boundary (`#[repr(C)]` mismatch, enum value out of the declared range, unwind across an FFI frame, thread-affinity violations when passing handles). The effect must be a real misread/misdispatch, not a stylistic portability note.
309
+ - Loader & image trust: dynamic-loader search order trusting a writable directory (`PATH`/`LD_LIBRARY_PATH`/relative rpath), verify-then-open mismatches (signature checked on one path, file loaded from another), and malformed metadata/relocations trusted by a custom loader. Classify as unauthorized image load only when the writable path reaches the load; an unverifiable runtime search order stays **requires runtime verification**.
310
+ - JIT & double-fetch: generated code consistency with the data it validates (bounds check compiled against one snapshot, executed against another), and double-fetch/user-copy patterns where shared or user memory is read twice with the size/bounds check between reads. A single read or kernel-side copy-in refutes.
311
+
312
+ **Effect classification:** claim only the effect the source shows — invalid read/write, stale alias reuse, wrong-object dispatch, uninitialized output, unauthorized image load, deadlock, or safe termination. Unprovable effects downgrade to a **Needs verification** lead (§12), never to confirmed.
313
+
314
+ **Exclusions (each on its own terms):**
315
+ - Stack allocation alone is not a leak — stack memory is reclaimed by return; a leak claim needs the buffer's address or contents escaping the frame.
316
+ - Language-permitted output variance (unspecified iteration order, float reassociation, hash seed differences) is not a vulnerability without a security-relevant consumer of the variance.
317
+ - Safe malformed-input rejection (parse error → clean abort) is not corruption; the finding requires malformed input accepted or misparsed into a wrong-but-valid state.
318
+
319
+ ### 9k. Availability & resource exhaustion
320
+
321
+ A finding requires the full chain: an **input → cost path**, an **absent effective upper bound**, and **harm to another principal, a shared service, or shared spend**. A bounded same-user cost alone is hardening. Check the whole path for existing bounds before blaming the missing service-local rate limiter — another layer (gateway limit, queue cap, DB constraint, tenant quota) bounding the path refutes.
322
+
323
+ - Input-to-cost bounds: body/message/file size limits, parse depth/complexity caps, and effective (not declared) upper bounds — a config constant that the actual path bypasses is absence, not a bound. **Exclusion:** an effective size/depth/complexity cap that the actual input path cannot bypass (the configured limit is enforced on the exact path the input takes, not an adjacent one) refutes.
324
+ - Superlinear parsing & ReDoS: quadratic string handling, backtracking regexes on user input (`(a+)+$` shapes on request paths), nested decode loops. A linear parser or a pre-size-gated input refutes.
325
+ - Decompression & amplification: multi-stage/multiplier decompression without output caps, query/fan-out amplification (one request triggering N upstream calls with attacker-chosen N). Bounded fan-out or an output-side cap refutes.
326
+ - Aggregate buffering & cardinality: per-item bounded but aggregate unbounded (unbounded in-memory accumulation, session/map growth, metric cardinality explosion from attacker-chosen label values). Eviction or a global cap refutes.
327
+ - Leak families: shared FD/handle/temp-file leaks on error paths, and work that survives cancellation (abandoned requests still consuming DB/worker capacity — a cancelled request whose query keeps running is the finding). Cleanup on the error/cancel path refutes.
328
+ - Asymmetric & pre-auth work: expensive operations (crypto, signature verification, password hashing) reachable pre-authentication where the cost ratio favors the attacker. Existing early cheap rejection refutes.
329
+ - Quota & reset semantics: quota windows/reset boundaries the caller can exploit (quota reset mid-burst, per-request quota ignoring aggregate spend), mismatch between the metered unit and the actual cost unit. Aligned metering refutes.
330
+ - Pool & supervisor scope: pool starvation by one tenant's slow work (connection/worker pools without per-principal fairness), fatal errors taking down a shared supervisor, retry storms on failure (unbounded retries amplifying an outage), poison records or head-of-line blocking (one malformed/repeatedly-failing item requeued ahead of healthy work, blocking or crashing the shared queue/worker pool — the finding is the poisoned item starving or cycling other principals' work), fail-open recovery, and capacity rollback that restores stale state. Fair scheduling or bounded retry with backoff — including a dead-letter/quarantine path for the poison record — refutes.
331
+
332
+ Never prescribe stress-testing or pressure shared/live services — this is static review of the cost path only.
192
333
 
193
334
  ## 10. Deployment & environment caveats
194
335
 
@@ -216,4 +357,25 @@ Apply where the repo actually has the surface. Absence is not a finding.
216
357
  - **Static evidence required:** every finding carries `file:line` and the code shape — the pattern plus the attacker-controlled input. No evidence, no finding.
217
358
  - Runtime-dependent claims are labeled exactly **requires runtime verification** and go to the audit index's **Needs verification** section — never reported as confirmed.
218
359
  - Findings use the standard finding format (**`references/finding-format.md`**); the Impact field must state the concrete attack scenario ("Send this request, get this result").
219
- - State what was NOT audited (effort level, unread packages, deployed-version assumptions) in the report, per the playbook's audit contract.
360
+ - Record what was and was not audited as Coverage rows in the report — one row per material review question, never a bare "not audited" disclaimer — per **`references/codebase-audit.md`** § Coverage contract.
361
+
362
+ ## 13. Per-class exclusion rules
363
+
364
+ Residual exclusion decisions not owned by an existing section. Each rule decides a class of signal; where a section already owns the check, this section points, it does not copy (§§2/5/6/7/9/10/11/12/14, §9i–§9k). Pointers name the most specific in-document owner; where the governing spec's §4 decision map names a coarser home (stored tokens → §9d vs §9h here; rate limits adding §9c), the deviation is intentional and this section governs. A demonstrated vulnerability is never exonerated by the mere presence of a common false-positive signal — confirm the concrete untrusted path and effect, or name the missing fact.
365
+
366
+ - **Self-injection / attacker's own data is not a finding alone — require an effect on another principal, origin, or protected shared state.** Self-XSS, a user corrupting their own records, or confusion over their own token affects no boundary. The decision follows §2's anti-strengthening rule (same-principal behavior is not privilege escalation); §13 applies it as the exclusion test: name the second principal, the other origin, or the protected shared state the payload reaches, or drop the finding.
367
+ - **Some permission check exists is not a finding alone — and it does not refute a bypass either.** The check must bind *this* principal, *this* resource, *this* action, and *this* entry path. A role check on the controller does not cover the sibling route; an `is_owner` filter on the list endpoint does not cover the per-item fetch; a middleware that authenticates does not authorize. Confirm the same check governs the exact path the attacker takes (§8 delegated-checks lens), or the bypass stands.
368
+ - **A crypto-error branch or write-only validation is not a finding alone — require untrusted data reaching the weaker read/fallback path and a resulting effect.** A failing-open encryption helper, a verify-then-use gap, or validation performed on the write path but not on a later read path is a lead until the flow is traced (§7): name the untrusted input, the path where the check is skipped or weaker, and the effect the weaker path enables. Safe failure (reject, abort, fail closed) refutes the claim; an untraced fallback stays a **Needs verification** lead (§12).
369
+ - **Pointers — each already owned, do not re-add:** missing headers/cookie attributes/rate limits → §2 defense-in-depth rule, §9c rate limits, §9k bounds · missing MFA/assurance gaps → §9a MFA enrollment & assurance · stored tokens/continuing use after revocation or logout → §9a session lifecycle, §9h stale authorization beyond sessions · identifiers named "key"/secret references → §6 symbolic-names rule, §9a publishable-vs-secret · internal network/schema/tenant labels and protocol disagreement → §14 · manifest-only inference → §9g evidence discipline, §10 · mutable/vulnerable dependencies and reproducible builds → §9f closing exclusion · same-user plugin installation → §9i intro · missing TTL/incomplete copies → §9h retention guarantees · generic timing variance → §9d XS-Leaks predicate · framework-escaped interpolation → §5 · missing sanitizer/seccomp/read-only filesystem → §11 OWASP-deviation rule, §9j exclusions · unknown version/configuration → §12 (Needs verification, never a downgraded confirmed claim).
370
+
371
+ ## 14. Protocol, RPC & messaging invariants
372
+
373
+ Grouped invariants for RPC frameworks, message queues, and event streams. Apply only where an RPC/queue/event surface exists; absence is not a finding. Groups contain related checks — this is not a second domain catalogue. Protocol disagreement alone is not a finding: **accepted unauthorized effect is required; safe rejection by either endpoint refutes that path.** Behavior that cannot be verified from source stays a **Needs verification** lead (§12), never a downgraded confirmed claim.
374
+
375
+ - **Peer identity and authority.** An internal network location, schema validity, or successful deserialization does not authenticate the producer or principal. Compare the verified envelope identity (mTLS peer, SASL principal, signed envelope) against body-claimed identity (`user_id`, `tenant`, `on_behalf_of` fields) and control-plane authority (admin topics, management APIs). **Exclusion:** an explicit authenticated binding — envelope identity verified and bound to the operation before dispatch — refutes the claim.
376
+ - **Endpoint coverage.** Authorization enforced on the primary method does not cover sibling surfaces: trace interceptors/middleware through streaming methods, reflection/health/metadata endpoints, and gateway-transcoded paths (REST-over-gRPC annotations, protocol bridges). **Exclusion:** absence of a reachable weaker path — every sibling route passing through the same interceptor chain or an equivalent check — refutes the bypass.
377
+ - **Resource and stream authorization.** Per-item authorization on unary calls must also hold per-item within continuing streams (server/client/bidi streams, batch consumers), and topic/queue ACLs must match the tenant model (a shared topic readable by any tenant's consumers). Payload tenant labels alone are not isolation — **but** a consumer that itself enforces tenant checks on every delivered message closes the path; an enforcing consumer refutes.
378
+ - **Correlation and ordering.** Correlation/request IDs must bind to the originating request and principal, not merely be present; then examine ack/commit ordering — accepting a stale, out-of-order, or replayed message into authorized state (idempotency-key without principal binding, offsets committed before the side effect, out-of-order writes winning). **Exclusion:** accepted *unauthorized* state is required — mere parser disagreement or rejected duplicates refutes.
379
+ - **Failure channels.** Dead-letter queues, error topics, and retry buffers inherit producers' sensitive payloads and are frequently readable by wider audiences than the source topic; retained secrets/PII in DLQs past the source's retention scope is the finding. **Exclusion:** private, correctly scoped diagnostics (DLQ ACLs matching or tighter than the source, redacted payloads) refutes disclosure.
380
+ - **Two-sided protocol enforcement.** Trace both producer and consumer sides of the contract, including replay and ordering validation on the consuming side — a producer that signs and a consumer that ignores the signature is an unenforced contract. **Exclusion:** safe rejection on *either* side (producer refusing to emit malformed frames, or consumer validating and dead-lettering them) blocks confirmation of the path; where the repo shows only one side, unverified behavior of the other stays a **Needs verification** lead (§12).
381
+
@@ -234,11 +234,12 @@ Default process artifacts are **gitignored** (`mstar-conventions`「Git 跟踪
234
234
  生命周期末端的物理回收(feature/integration worktree、本地/远端分支删除)的 ownership 与守卫规则**只在本节**;两条时序车道的 call site(Phase-2 同轮 / Phase-6 收尾)只引用本节,不复制规则。命令(**dry-run 默认**;无 fetch / prune / 任何写入):
235
235
 
236
236
  ```text
237
- mstar worktree cleanup --workflow <id> [--harness <path>] [--apply] [--remote] [--worktree <path>] [--ignore-unreadable-snapshots]
237
+ mstar worktree cleanup --workflow <id> [--harness <path>] [--apply] [--remote] [--worktree <path>] [--all-workflows] [--verbose] [--ignore-unreadable-snapshots]
238
238
  ```
239
239
 
240
240
  - dry-run 逐候选打印 `verdict | kind | ref | reason` 后结束;`--apply` 只执行当前 `remove` 行。Exit:0 = 合法 dry-run / eligible 移除全部成功;1 = 探测/变更失败;2 = usage。失败行**永不扩大范围**;受保护/拒绝行保持可见。
241
- - `--worktree <path>` 可重复:既收窄 worktree 候选集,也是**操作者所有权断言**——必须匹配记录的生命周期分支与同仓 checkout 身份,不能认领其他 lifecycle 的 worktree。`--remote` 只决定是否纳入 `origin/*` 删除候选;安全探测(integration 证据)无论是否 `--remote` 都会收集。
241
+ - **候选范围(默认 scoped)**:默认候选只来自**选中 workflow 的记录归属**(snapshot 行元数据或已验证的 `--worktree` 断言);`--all-workflows` 恢复**全量扫**(含未记录 ref 与所有 workflow 的候选)。`--worktree <path>` 可重复:既收窄 worktree 候选集,也是**操作者所有权断言**——必须匹配记录的生命周期分支与同仓 checkout 身份,不能认领其他 lifecycle 的 worktree;带 `--worktree` 时本地分支候选进一步收窄到与保留断言 owner 精确同属的分支(无命中的断言 owner ⇒ 无分支候选,**没有**全量回退;missing / 越界的断言路径只产出一条 note,不构造目标)。
242
+ - `--remote` 只决定是否纳入 `origin/*` 删除候选;**本地**合并证据(`git branch --merged <base>` 成员资格)**无论是否 `--remote` 都会无条件收集**,**远端** integration 证据仅在使用 `--remote` 时收集。证据探测**有界**:base 解析一次、每个去重 base OID 至多一组 membership sweep + pass 内不可变备忘录,`--verbose` 追加逐对 ancestry 诊断(不改变候选与判定)。
242
243
  - **信任模型**:`--harness <path>` 为操作者提供且受信——dry-run 与 `--apply` 的全部状态事实(snapshot、lease、行归属元数据、protected 锚点)均读自该目录。
243
244
  - **坏 sibling 不再阻塞,且不丢保护**:扫描 `workflows/*/snapshot.json` 时,**非选中**的坏 snapshot 不会让命令失败(exit 1)。**JSON 可解析但校验失败**者以**降级保守形态**入安全集:只携带具保护性的声明(`branch.base` / `branch.integration` / `branch.target`、lifecycle worktree path、merge / execution lease、行 ownership 元数据),且 lifecycle 与行状态一律强制为非终态——故只会**增加** keep/refuse 判定,绝不减少(它保护的分支/worktree 会被 `cleanup.keep.protected-ref` 或 `cleanup.refuse.*` 拦住)。**完全不可解析**者声明不可知:默认 **withhold 全部 remove**(改判 `cleanup.refuse.unreadable-snapshot`,plan 仍完整打印),仅当操作者给出 `--ignore-unreadable-snapshots` 断言时才按可读 snapshot 判定。**选中** workflow 自身 snapshot 不可读仍是探测失败(exit 1);任何坏 snapshot 的字节**永不**被修复、改写或删除。
244
245
 
@@ -44,6 +44,11 @@ description: Morning Star 派发与委派门禁 —— 仅 PM 可增派 subagent
44
44
  - **credential 不下发 leaf**:session JSON 路径、`mstar plan --session` 写凭据、`--expect <revision>` 等**只由派发方(PM/coordinator)持有**。leaf 拿到 session 路径或写凭据即视为越权 → 停止并回报(`mstar-iteration/references/plan-scoped-pm.md` §8)。
45
45
  - **`project-manager` 不是派发目标**:PM 是 primary-session 角色,无 subagent shell(规则家 → `mstar-roles/references/project-manager.md` § Plan-scoped authority;宿主派发面 → 当前宿主的 **`mstar-host` reference**(角色绑定 / 派发小节));scoped primary drive(`/iteration-drive --assignment | --workflow --plan | --resume`)在**主会话**启动 PM,不是 subagent。任何 `Execute as: project-manager` 的 invoke = 派发缺陷。
46
46
 
47
+ ## 跨会话 primary 并发:唯一受支持形式(scoped route)
48
+
49
+ - 跨**独立主会话 / 终端**的并发只有一种受支持形式:**scoped route** —— 由已准备(prepared)的 Assignment 作为**全新会话的第一条指令**启动该会话。通过终端提示词下发自拟的 leaf Assignment **不是**受支持的派发路径:它绕过 scoped 启动、lease 归属与 handoff 交接。
50
+ - 本边界不取代宿主的原生 leaf 派发,也不重复 N-invocation 机制(→ **`mstar-host`** → `references/parallel-dispatch.md`);该路线的操作前置清单由宿主 reference 独有承载。
51
+
47
52
  ## 调度防串扰(强制;leaf executor 已在上方读过反递归红线,此处为完整规则供 PM/对照用)
48
53
 
49
54
  - 只有 **`project-manager`** 可以决定增加/并行 subagent;承接方**默认不得二次分派**。
@@ -111,6 +116,7 @@ When **`Execution mode: sdd`** (`mstar-sdd`):
111
116
  当 PM 派发**文档编辑类**专业角色(如 product-manager、architect、writing-specialist)直接修订 harness 产物时:
112
117
 
113
118
  - PM 写初稿;各角色通过宿主 invoke **直接编辑**目标文件(**不**另写仅评论式 `reports/` 替代修订)。
119
+ - **`Inputs`(Phase 1 review-and-edit Assignment)**:PM 初稿 = **骨架 + 完整上下文**;深度契约、marker 语法与 owner 词汇 → **`mstar-iteration/references/phase-1-prepare.md`** §1.3(本文件不重述其形态)。该轮 Assignment 的 **`Inputs`** 必须携带:初稿路径(+ compass 路径);方向决策 —— 或指向 compass `## Decisions` 的指针;带 owner 的 open questions —— 或指向 `## Open Questions` 的指针;非目标理由;**该角色 own 的 marker 清单**(`TODO(owner: …)`;清除义务随清单下发 —— 承接方须在自己的 Completion Report 中报出**清除计数 / 重新归属计数**,义务全文 → **`mstar-iteration/references/phase-1-prepare.md`** §1.6)。承接方看不到 PM 会话:这些字段缺一,角色就只能从零重推它看不见的上下文。
114
120
  - **1 Assignment ⇒ 1 invoke**。
115
121
  - **Phase 1 Review & Edit chain**(`mstar-iteration` §1.6):主产出 **`{SPECS_DIR}/`** + **`{ITERATION_DIR}/<iteration-id>/`** package;**禁止** start 链向 `{KNOWLEDGE_DIR}/` 新增。close 时 **`mstar-compound`** 提升 package → knowledge。
116
122
  - 其他彼此独立、无先后依赖的文档编辑任务:可并行(同条消息发满 N),见 **`parallel-dispatch.md`**。
@@ -127,6 +133,7 @@ When **`Execution mode: sdd`** (`mstar-sdd`):
127
133
  - 递归同角色 subagent;把 Handoff / 多轨编排措辞当 invoke。
128
134
  - Review-and-edit 链未完成即 commit integration 分支;PM 代做专业角色编辑而不 invoke。
129
135
  - Phase 1 review-and-edit 链三角色并行派发,或未等上一角色返回即派发下一角色。
136
+ - 派发 Phase 1 编辑角色时不给方向决策 / open questions(或只写「见 compass」而不给路径)⇒ 承接方被迫重推它看不到的上下文,且其 own 的 marker 无人清除。
130
137
  - Assignment 已写、invoke 为零(paste-only)却进入下一 gate。
131
138
  - Task/subagent item 漏写角色绑定字段(字段名与静默回退行为以当前宿主的 `mstar-host` reference 为准)⇒ **静默回退 generic worker**,却因 count=N 通过而误判「派发完成」;属 paste-only 同级的 **dispatch-incomplete**。N=1 顺序 Review-&-Edit 链最易在此漏字段。
132
139
 
@@ -100,7 +100,7 @@ Cursor’s live **Task** tool expects a **flat** JSON argument object. `prompt`
100
100
  | `prompt` | yes | Full Assignment Markdown string (IDENTITY, gates, Plan Path, Working branch, …) |
101
101
  | `subagent_type` | yes | Must equal Assignment `Execute as` (Morning Star role id when using custom agents) |
102
102
  | `description` | recommended | Short UI title only (3–5 words); never the Assignment body |
103
- | `model` | optional | Host slug from Assignment **Model tier** mapping |
103
+ | `model` | optional | Host slug for the **session's own model policy** — host-owned and optional; mstar prescribes no tier. Omit to inherit the host default |
104
104
  | `run_in_background` | optional | Default false unless PM intentionally backgrounds |
105
105
  | `resume` | sticky continue only | Omit on fresh dispatch; see sticky section |
106
106
 
@@ -169,18 +169,6 @@ Implementation roles use `mstar-coding-behavior` for RCA, test-first checks, rev
169
169
  - Single-seat and tri-review need identical `plan_id` and review scope fields.
170
170
  - Task parallelism does not relax branch/worktree isolation.
171
171
 
172
- ## Model tier (SDD + QC)
173
-
174
- Map Assignment **`Model tier`** to Task `model` (host-specific slugs):
175
-
176
- | Tier | Typical use |
177
- |------|-------------|
178
- | `fast` | Transcription tasks; 1–2 file mechanical edits |
179
- | `standard` | SDD prose implementer; task reviewer floor; plan QC tri seats |
180
- | `capable` | Large branch QC diff; integration judgment |
181
-
182
- **Turn count beats token price** — reviewers and prose implementers use `standard` floor minimum. See `mstar-sdd` SKILL.
183
-
184
172
  ## Project rules
185
173
 
186
174
  Project `AGENTS.md` / `CLAUDE.md` upward from cwd override global harness defaults when they conflict with Cursor-side rules; user instructions win.
@@ -267,19 +267,30 @@ Not a user activation command: nothing is spawned, merged, leased or written to
267
267
 
268
268
  1. **`bind`** — the **`phase-2-entry`** anchor (→ **§ Host hooks**): the coordinator's **Phase 2 execute/resume entry** (immediately before the per-plan loop, after the §2.0 gates; §2.3's Phase-1-reused integration-worktree step is not this anchor), independent of any setting or model handoff, and also required on a no-argument `/iteration-drive` resume. Call shape, authority derivation, the rejection set and its refusal codes live in that section.
269
269
  2. **`checkpoint`** — the **`rescheduling-checkpoint`** anchor (→ **§ Host hooks**): acknowledge that PM ran the shared scheduling procedure against the sample taken at that moment, with `reason` from the five frozen names and `decision` ∈ `dispatched | wait | blocked`. The runtime attaches the sampled key; the caller cannot choose or reset it. `blocked` suppresses advisory continuation until a new explicit user turn or a later checkpoint clears it — never a timer or incidental snapshot churn. A checkpoint carries a decision and note, never a ready list.
270
- 3. **`reserve-launch`** — `{operation:"reserve-launch", planId, transport:"herdr"|"tmux", skill:{name,source}, capability:{executable,version,target}}`. Admission additionally requires the enabled opt-in, a valid latest capacity, a coordinator-prepared row with no plan binding/lease/handoff, the identical prepared Assignment hash, an existing canonical **distinct** feature worktree on its assigned branch, and the transport prerequisites. Only `applied:true` authorizes a side effect; an identical duplicate returns the recorded intent with `applied:false` and authorizes nothing, while a different live intent/binding for that plan refuses. Intents are journaled at `<workflow dir>/omp-launches.json` — a plugin-owned transport journal, never a lifecycle register.
270
+ 3. **`reserve-launch`** — `{operation:"reserve-launch", planId, transport:"herdr"|"tmux", skill:{name,source}, capability:{executable,version,target}}`. Admission additionally requires the enabled opt-in, a valid latest capacity, a coordinator-prepared row — prepared by **`mstar plan prepare`**, the coordinator step that writes `coordination.prepared` and its pinned Assignment path (verb preconditions and sequence → `mstar-use-cli/references/plan-and-workflow.md`) — with no plan binding/lease/handoff, the identical prepared Assignment hash, an existing canonical **distinct** feature worktree on its assigned branch, and the transport prerequisites. Only `applied:true` authorizes a side effect; an identical duplicate returns the recorded intent with `applied:false` and authorizes nothing, while a different live intent/binding for that plan refuses. Intents are journaled at `<workflow dir>/omp-launches.json` — a plugin-owned transport journal, never a lifecycle register.
271
271
  4. **`record-launch`** — one transition per observed step, each recorded **before** its matching side effect: `{operation:"record-launch", intentId, observation, target?, evidencePath}` with `observation` ∈ `starting | created | submitting | submitted | refused | uncertain`. Strict forward order `reserved → starting → created → submitting → submitted`; `starting` permits pane creation, `created` (which **requires the returned opaque target**) permits starting OMP in it, `submitting` permits the single scoped prompt. Only a newly persisted transition reports `applied:true`; PM acts only on that. `refused` is legal only for an observed failure that provably precedes any process/prompt side effect; from `submitting` onward a lost outcome is `uncertain`, which is terminal. A recorded target is never re-pointed, and settings/capacity/ownership are re-read before each side-effecting transition.
272
272
 
273
+ Ordered summary of the extra-primary route: the plan row is **registered during Prepare** and still `Todo` → its assigned feature worktree exists → the coordinator is bound → **`mstar plan prepare`** prepares the row and produces the prepared Assignment → `reserve-launch` (item 3) → the record-before-side-effect transitions of item 4, then the pane/start/submission sequence in **§ Optional transport** below. Launch through this journaled sequence only; never invent a shorter unjournaled path. Row admission closes with Phase 1 — earlier once any row starts preparation or execution — so a row that was not admitted by then does not exist and cannot be launched. `mstar plan prepare` itself has no phase gate: it requires only a claimable unprepared row, so an already-registered eligible row may be prepared during Phase 2. Row admission and row preparation are different operations, and nothing here widens an engine rule. The host-agnostic statement of this precondition lives in `mstar-iteration/references/plan-scoped-pm.md` § Transport.
274
+
273
275
  Refusal codes for `reserve-launch` / `record-launch` — **not** anchor-triggered (the optional extra-primary path carries no shared anchor moment, so these belong to no anchor row): `launch.invalid-request`, `launch.session-denied`, `launch.phase-inactive`, `launch.snapshot-unreadable`, `launch.journal-corrupt`, `launch.plan-not-found`, `launch.plan-unavailable`, `launch.plan-not-prepared`, `launch.plan-occupied`, `launch.prepared-hash-drift`, `launch.worktree-unavailable`, `launch.capability-unavailable`, `launch.settings-disabled`, `launch.settings-invalid`, `launch.settings-read-failed`, `launch.capacity-exceeded`, `launch.intent-not-found`, `launch.transition-invalid`, plus the shared `tool-error`. Every one is a visible refusal that authorizes nothing; success codes are `reserved` / `recorded`, and `replayed` (`applied:false`) for an identical duplicate that likewise authorizes no side effect.
274
276
 
275
277
  ### Optional transport (skill-driven — no compiled bridge)
276
278
 
277
279
  Every prerequisite is required and checked per launch: the **corresponding optional skill actually present in the catalog and read** (the `herdr` skill today; a tmux skill only if one truly exists — binary existence is not skill availability), the CLI executable available, and this session actually inside the matching managed environment (`HERDR_ENV=1`; `TMUX` set for tmux). Two managed environments visible at once is a visible refusal, not a focus-based choice. A missing prerequisite is a **visible no-op**: no process start, no silently substituted plan, native background scheduling intact, never a fabricated success. This never becomes a mandatory load-order dependency of the standalone `mstar-*` skill set.
278
280
 
279
- **Herdr** (the currently available contract — read the skill and its current group help/status at use): create the pane with `herdr pane split --current --direction <chosen> --cwd <prepared-worktree> --no-focus` and use the returned `result.pane.pane_id` verbatim as the opaque target; then `herdr agent start <unique-name> --kind omp --pane <returned-id>`; then submit exactly once with `herdr agent prompt <unique-name> "/iteration-drive --assignment <absolute-prepared-assignment>"`, without waiting for plan completion. Preserve CLI argument boundaries.
281
+ **Herdr** (the currently available contract — read the skill and its current group help/status at use): create the pane with `herdr pane split --current --direction <chosen> --cwd <prepared-worktree> --no-focus` and use the returned `result.pane.pane_id` verbatim as the opaque target; then `herdr agent start <unique-name> --kind omp --pane <returned-id>`; then submit exactly once with `herdr agent prompt <unique-name> "/iteration-drive --assignment <absolute-prepared-assignment>"`, without waiting for plan completion. Preserve CLI argument boundaries. `<absolute-prepared-assignment>` is the absolute `coordination.prepared.assignment_path` pinned when the coordinator ran **`mstar plan prepare`** for this row (see the ordered summary under **§ PM call sequence**); submission is defined only for a row whose preparation already produced that artifact, never for a merely registered one. *Exactly once* bounds the **initial** command only: any further context is steering sent after the session has bound (its `InProgress` row and execution lease identify the new session) — sent earlier it either duplicates the command or becomes the session's first instruction.
280
282
 
281
283
  **tmux** (conditional): only where a matching tmux skill is actually present **and read**, the CLI supports that skill's command forms, `TMUX` identifies this caller, and its explicit target is resolved. Use the skill's detached/non-focus creation with explicit cwd and returned pane id, launch OMP in it, and submit the same absolute scoped route; inspect help instead of guessing flags, and never shell-type into the user's focused pane. **No tmux skill exists in the current catalog**, so tmux is unavailable here — an unsupported seam to be named honestly, not a failed implementation and not a silent Herdr substitution.
282
284
 
285
+ **Scoped-route dispatch checklist** — every item is checked at this handover point; each states its reason:
286
+
287
+ 1. Target a **fresh** session, never one already acting as a leaf: the harness itself refuses promotion (`commands/iteration-drive.md:32` refuses a leaf that receives the scoped command), and the field observation is that a session's role is fixed by its first instruction — that is the operational reason for the rule, not a universal transport API guarantee.
288
+ 2. Confirm the row is **prepared, not merely registered**: `coordination.prepared` carries the pinned Assignment path, a current `coordination.revision` is observed, the row is still `Todo`, and there is no plan session/lease/handoff. A revision alone is insufficient. The coordinator runs **`mstar plan prepare`** at this handover point.
289
+ 3. Use the absolute prepared Assignment path, and finish handover content **before** preparation so the prepared bytes survive the fresh bind. Both SHA-256 fields are stored at **prepare**, not first created at bind (`coordination.ts:1882-1884`); bind rechecks the Assignment hash and refuses `coordination.assignment-stale` when it changed (`:1094-1108`, `:1582`). Current source does not recheck `plan_sha256`, so the user-reported plan-edit refusal stays **mechanism-unconfirmed** — not a new engine guarantee and not an eternal ban on authorized plan evidence updates.
290
+ 4. Submit the initial scoped command **exactly once**; no preliminary leaf/role prompt and no coordinator credentials.
291
+ 5. Read the returned explicit target to confirm execution — submission is not execution. The reported trailing Enter being consumed before readiness is an **observation about a CLI this repository does not own**. Only if the readback unambiguously shows the same command waiting unexecuted, recover with one Enter/key press using the optional skill's verified command form; never resubmit. Otherwise stop with uncertainty: a timeout, a lost response and `agent_not_ready` remain terminal, not retry opportunities.
292
+ 6. Send subsequent handover notes/corrections only as **steering**, after `InProgress` and the execution lease identify the new session; never prepend them as another first instruction.
293
+
283
294
  **Uncertainty, scope and ownership**: PM uses only returned opaque targets, records the observed command output before proceeding, and treats `agent_not_ready`, blocked UI, a timeout, a vanished response or a stalled submission as terminal — reported, never re-sent, never retried, with no fabricated id and no credential passed (no session JSON path, no `--expect` revision, no `--resume`). A created empty pane may be removed only with proven ownership and no possibly-active primary; panes are never killed to free capacity. Pane ready/idle/done means prompt transport is ready — never plan completion, lease release or ownership. The child obtains its own engine session through a fresh `plan bind`, runs only the prepared scope and stops at its durable handoff; the coordinator alone keeps serial integration and Phase 3–6 closure.
284
295
 
285
296
  **Evidence boundary**: this transport guidance is supported by **simulated** scripted skill/CLI observation traces (PM action sequences scored for command order, prepared cwd, non-focus creation, credential absence, opaque-target reuse, stopping on uncertainty and no blind resend) — not by a native end-to-end Herdr/tmux run, not by any probe of user terminals, and not by a real OMP child process.
@@ -297,6 +308,12 @@ Carrier moments — where the PM meets the marker:
297
308
  | `phase-2-entry` | `mstar-iteration/references/phase-2-worktree-lease.md` immediately before the `## 2.4 Per-plan loop` heading (the Phase 2 execute/resume entry after the §2.0 gates) |
298
309
  | `rescheduling-checkpoint` | `mstar-iteration/references/phase-2-worktree-lease.md` §2.4 (`### Rescheduling checkpoint`) |
299
310
 
311
+ ### Session identity association (host → `plan bind`)
312
+
313
+ Two comparisons this surface makes — the `phase-1-lock` readiness checkpoint and the `iteration-entry` start-authority scan — compare the **engine** session id with the **host** session id, so they are one identifier only when the engine was told which one to adopt. The model-handoff extension closes that gap on the host side: each `bash` tool call of its session is revised to carry the host session id in **`MSTAR_HOST_SESSION_ID`** — an overwrite of any caller-supplied value under that name, `bash` only, nothing injected for a host session with no id, and no engine or harness write, notice or state. A fresh `mstar plan bind` resolves its identity **`--session-id` → `MSTAR_HOST_SESSION_ID` → the engine-generated id**: the flag wins and is taken verbatim, the injected variable is the fallback (trimmed; empty or whitespace-only counts as absent), and the generated default is unchanged. One host session may hold both sessions a workflow needs — a coordinator session and a plan-pm session — both carrying the same host-derived `session_id`; the engine distinguishes the two envelopes by the role-scoped file name (`<role>-<session-id>.json`). `--resume` never re-identifies and refuses `--session-id` as a usage error.
314
+
315
+ No comparison is relaxed. An **absent** association (nothing injected, or a host session with no id) and a **foreign** one (the id belongs to another session) still refuse: readiness keeps failing its `binding-invalid` code, the start-authority refusals (`task-session`, `plan-pm-session`, `coordinator-elsewhere`) keep their codes, and an id the engine cannot use as the envelope's file name is refused with `coordination.invalid-session-id` before any write. Other hosts are unaffected — this extension is the only producer of the variable, so a bind elsewhere keeps the engine-generated id unless the variable is set in that session's environment.
316
+
300
317
  ### Auto-trigger boundary — the diagnosed failure mode
301
318
 
302
319
  Neither extension ever arms, binds or checkpoints by itself:
@@ -306,6 +323,19 @@ Neither extension ever arms, binds or checkpoints by itself:
306
323
 
307
324
  Enabling `modelHandoff` or `phase2PlanInstances` in `/settings` **never retro-arms or retro-binds** an iteration already under way (the preference is re-read at entry *and* again at fire time), and disabling it suppresses the action without terminalizing the binding. A coordinator that never calls therefore produces **no state and no signal at all** — not a refusal, not a warning, not a log line. That silence is the failure mode this anchor contract fixes: both mechanisms shipped with zero PM call sites in the load chain, so every anchor below is an explicit **required** call, never something the host does for the PM.
308
325
 
326
+ ### Coordinator-visible notices
327
+
328
+ Both mechanisms emit durable, coordinator-visible notices, and each family keeps its existing custom type: **`mstar:phase2-notice`** for the Phase-2 observation's diagnostics (`extensions/phase2-orchestration.js`) and **`mstar:model-handoff-notice`** for coordinator model-handoff transitions (`extensions/model-handoff.js`). Neither type is renamed, re-typed or extended elsewhere.
329
+
330
+ Every notice is rendered through the shared Morning Star title shape as `<title>: <detail>`, with the observed condition or refusal code preserved in the detail:
331
+
332
+ - **Status-bearing title** — when the workflow's own snapshot was successfully read at notice time, the title states a Morning Star status: the workflow id plus its actual lifecycle status, verbatim (`Workflow <id> is <status>`). It reports workflow lifecycle status only — never `snapshot.phase`, a refusal code, or the model-handoff ledger state (`pending`, `cancelled`, `handed_off`).
333
+ - **Fallback title** — when no readable snapshot supplied the id/status, the title states only the observed operation or condition (`… needs attention`) and asserts no workflow status. Missing or unreadable status evidence never suppresses a notice and never substitutes a guessed `running`/`completed` value.
334
+
335
+ A terminal lifecycle no longer produces a notice asserting the Phase-2 phase: a terminal workflow refuses `phase2.workflow-terminal` with its observed status, and no notice or refusal text on either path carries a fixed inactivity prefix or a detail sentence asserting a current Phase-2 position.
336
+
337
+ The Phase-2 diagnostic path stays deduplicated by code per generation — at most one notice per distinct refusal code until the generation resets. The model-handoff notices do not share that dedup mechanism.
338
+
309
339
  ### Anchor declarations
310
340
 
311
341
  | Anchor | Call (exact) | When | Prerequisite | Required |
@@ -318,10 +348,11 @@ Enabling `modelHandoff` or `phase2PlanInstances` in `/settings` **never retro-ar
318
348
  #### `iteration-entry` — `mstar_model_handoff {operation:"start", workflowId}`
319
349
 
320
350
  - **Exact parameters**: `workflowId` (string, required) — the only field a caller supplies that matters here; `coordinatorSessionPath` / `mainWorktreeBranch` are accepted by the schema but unused on this path. Authority, entry route, intent and task-session state are host-derived, never call-declared.
351
+ - **Two structural routes (host-derived)**: the public tool keeps `{operation:"start", workflowId}` — no mode, session credential or authority claim is accepted from the caller. The adapter classifies the branch from the validated root register for that explicit id, never from a newest/unique-workflow inference. **Unregistered workflow** → the unchanged reservation path: root validation unchanged, and the three existing-artifact refusals keep their code and byte-identical messages — `already-bound`: `workflow <id> is already registered in the root register — a new start never adopts it` · `a workflow snapshot already exists at <snapshotPath>` · `an iteration compass already exists at <compassPath>`. **Already-registered workflow** → attach: the named active register row (exactly one) and the workflow's actual own snapshot, identity and canonical paths are revalidated rather than trusted from the branch classification — on this branch an existing own snapshot/compass is expected, not an adoption refusal — and the start proceeds through the unchanged one-shot arm protocol to `pending` only after the existing authority derivation allows this session. Attach structural failures refuse through the existing vocabulary — `invalid-root` (absent register, vanished or malformed row, missing or mismatched snapshot, wrong canonical path), plus the pre-existing `not-coordinator` / `invalid-workflow` input checks unchanged — with no fallback into reservation and no state write. No new refusal code and no public tool mode is introduced by either branch.
321
352
  - **When**: once the workflow is registered to the v2 status surface and its **id is known** (`mstar-iteration/references/phase-1-prepare.md` §1.5 tail) — the PM's first preparation action that can name it. Never at the head of §1.1: the id does not exist yet, and the id must name a registered workflow so its coordinator envelope exists.
322
353
  - **Required or optional**: **required** and unconditional — make the call even when the preference is off, because `preference-off` is then the expected non-fatal answer and its absence from the ledger is what makes the silence undiagnosable. One call per iteration; a second arm of the same workflow is refused.
323
- - **No-op refusals** (visible in the tool result, **nothing changed**, model unchanged — not failures to fix beyond the stated cause): `preference-off` (not an error) · `already-bound` · `suspended` (state `none`; a navigation is in flight, retry in a moment) · `in-flight` / `arm-in-flight` (a previous handoff action is still running).
324
- - **Authority refusals** (this session is not the iteration coordinator — fix the session, not the call; no model action, no binding written): `task-session` (leaf/subagent session, or a session with no id) · `scoped-plan-route` (the last observed entry was the scoped-plan PM route, which restores a binding and never arms a new one) · `plan-pm-session` · `coordinator-elsewhere` (this session coordinates another workflow, or the workflow belongs to another session) · `envelope-invalid` · `register-invalid` (unreadable or invalid v2 root register / workflow snapshot).
354
+ - **No-op refusals** (visible in the tool result, **nothing changed**, model unchanged — not failures to fix beyond the stated cause): `preference-off` (not an error) · `already-bound` (the three reservation refusals under **Two structural routes** above; a pending/uncertain/terminal same-workflow binding in this session's ledger — a terminal binding is never re-armed; or, on the attach branch, the foreign-coordinator outcome described under **Authority refusals** below) · `suspended` (state `none`; a navigation is in flight, retry in a moment) · `in-flight` / `arm-in-flight` (a previous handoff action is still running).
355
+ - **Authority refusals** (this session is not the iteration coordinator — fix the session, not the call; no model action, no binding written): `task-session` (leaf/subagent session, or a session with no id) · `scoped-plan-route` (the last observed entry was the scoped-plan PM route, which restores a binding and never arms a new one) · `plan-pm-session` · `coordinator-elsewhere` (this session coordinates another workflow, or the workflow belongs to another session) · `envelope-invalid` · `register-invalid` (unreadable or invalid v2 root register / workflow snapshot). The derivation and its six-code vocabulary are the same on both routes. One approved observable mapping exists on the attach branch only: when the named registered workflow's own coordinator envelope names a different session, that decision — carrying an internal typed discriminator, itself not a refusal code or exported symbol — surfaces as `already-bound` with the original detail, `workflow <id> is bound to coordinator session <sessionId>, not to this session`. Every other authority failure keeps its original code on both routes, including `coordinator-elsewhere` when this session coordinates a different workflow.
325
356
  - **Visible arm failures** (reported as failures; **no automatic retry**): `settings-read-failed` · `record-failed` · `slow-unresolved` · `slow-selection-failed` · `slow-selection-refused` · `arm-evidence-conflict`.
326
357
  - **Host-level**: `tool-error` (the tool threw without touching model, ledger or engine state).
327
358
  - **Success code**: `armed` (state `pending`) — the coordinator holds it until `phase-1-lock` fires or the handoff is cancelled.
@@ -330,7 +361,7 @@ Enabling `modelHandoff` or `phase2PlanInstances` in `/settings` **never retro-ar
330
361
 
331
362
  - **Exact parameters**: `workflowId` (must equal the bound workflow) · `coordinatorSessionPath` (non-empty string) · `mainWorktreeBranch` (string — the recorded integration branch) · `reviews[]` — **exactly three ordered specialist returns** · `plans[]` — **at least one** bound-plan evidence entry. A missing or wrong-length input is refused, never inferred.
332
363
  - **When**: Phase 1 completion — **after** the integration worktree exists (recorded `integration_worktree_path`), the reviewed changes are committed on that checkout and `spec_integration_branch` is pushed: the tail of the §2.3 integration-worktree checklist, whose **step 7** performs that transfer + commit + push (Phase 1 reaches it through `iteration-start` §6). The compass/PM lock alone is **not** the moment — it leaves readiness items 3–4 unmet, so `not-ready` returns, the binding stays `pending`, and no later marker retries it. The call is made once, on the Phase 1 route; a Phase 2 resume that walks the same section must not repeat it — the binding is already terminal, so a repeat call returns `not-pending` (flagged as an error, since no `pending` binding exists any more) and is not required.
333
- - **Required or optional**: **required**, and never a silent skip — every refusal lands in the tool result, and every state transition additionally as a durable session notice.
364
+ - **Required or optional**: **required**, and never a silent skip — every refusal lands in the tool result, and every state transition additionally as a durable session notice under `mstar:model-handoff-notice` (title semantics → **Coordinator-visible notices** above).
334
365
  - **Readiness prerequisite (all four, re-checked at fire time)**: the sequential specialist returns for that iteration; the PM-confirmed Prepare gate for every registered plan with `compass status: locked`; a distinct same-repository integration checkout on its recorded branch; a remote tip equal to the validated integration HEAD. `evaluatePhaseGate` is a later-phase gate and is never readiness evidence; a draft compass, a lock alone, a missing checkout or an unpushed commit is not ready.
335
366
  - **Refusals that leave the binding `pending`** (fix the stated cause and call again — **nothing was switched**): `not-ready` (carries `codes[]` naming the unmet readiness facts; not an error) · `preference-off` (`modelHandoff` off at fire time; not an error) · `settings-read-failed` (read once before and once after the readiness work) · `suspended` · `in-flight` · `record-failed` · `not-pending` (no binding — run `iteration-entry` first) · `binding-mismatch` (the call names a different workflow) · `invalid-completion-input` (the evidence shape above).
336
367
  - **Terminal refusals** (**no retry**; the session keeps the model it actually has): `cancelled` (an unowned model change arrived while pending; not an error) · `target-unresolved` (`handoffTarget` unresolvable) · `switch-refused` / `switch-threw` (the host refused the selection).