/**
 * Analyse-verify detectors — pure predicates shared with the demo-self harness.
 *
 * `isProgrammingStep` / `programmingSignals` are a verbatim port of
 * `specverse-demo-self/harness/scoring/step-abstraction.mjs` (TODO #92): the
 * measurement scorer and the enforcement verifier share ONE predicate, so a
 * rule provably matches what the regression metric measures. Keep the two in
 * sync if either changes.
 *
 * `isNonDomainEvent` flags a `publishes` entry whose NAME reads as a query /
 * read / render / helper rather than a domain state-change. We deliberately do
 * NOT use subscriber-count: a published event with no in-spec subscriber is
 * not dead (external services / event bus / webhooks may consume it), so the
 * discriminator is the event KIND encoded in its name.
 */
// Common non-code "word.word" tokens to NOT treat as member-access.
const ABBREV = /\b(e\.g|i\.e|etc|vs|a\.m|p\.m)\b/i;
const KIND_PREFIX = /^\s*\[[a-z]+\]/i; // [call] [branch] [return] [assign] [catch] …
const CALL = /[A-Za-z_$][\w$]*\([^)]*\)/; // foo(...) / res.status(401)
const MEMBER = /\b[a-z][\w$]*\.[a-z][\w$]*/; // dto.customerId, req.params, res.status
const STATUS = /\b[1-5][0-9]{2}\b/; // HTTP status code
const OPERATORS = /===|!==|=>|\?\?|&&|\|\||\.then\b|\.map\(|\.filter\(|\{\s*\w+:/; // code operators / JSON literal
/** True if a step reads as transcribed code rather than business intent. */
export function isProgrammingStep(step) {
    const s = String(step || '');
    if (!s.trim())
        return false;
    if (KIND_PREFIX.test(s))
        return true;
    if (CALL.test(s))
        return true;
    if (STATUS.test(s))
        return true;
    if (OPERATORS.test(s))
        return true;
    if (MEMBER.test(s.replace(ABBREV, '')))
        return true;
    return false;
}
/** Which signal(s) fired — for diagnostics + the correction message. */
export function programmingSignals(step) {
    const s = String(step || '');
    const sigs = [];
    if (KIND_PREFIX.test(s))
        sigs.push('pseudocode-kind-prefix');
    if (CALL.test(s))
        sigs.push('function-call');
    if (STATUS.test(s))
        sigs.push('http-status-code');
    if (OPERATORS.test(s))
        sigs.push('code-operator');
    if (MEMBER.test(s.replace(ABBREV, '')))
        sigs.push('code-member-access');
    return sigs;
}
/** Event-name suffixes that denote a query / read / render / helper rather
 *  than a domain state-change. Anchored to the end of the (Pascal/camel) name
 *  so `Read`/`Listed`/`Fetched` etc. match only as the trailing verb. */
const NON_DOMAIN_EVENT_RE = /(?:Fetched|Read|Listed|Loaded|Viewed|Rendered|Displayed|Shown|Dismissed|Normalized|Normalised|Calculated|Computed|Mapped|Parsed|Validated|Checked)$/i;
/** True if an event NAME reads as a non-domain (query/render/helper) event. */
export function isNonDomainEvent(eventName) {
    const s = String(eventName || '').trim();
    if (!s)
        return false;
    // Strip a leading model qualifier (`Order.created` → `created`); judge the
    // event part. Dotted form is the canonical `Model.event` shape.
    const eventPart = s.includes('.') ? s.slice(s.lastIndexOf('.') + 1) : s;
    return NON_DOMAIN_EVENT_RE.test(eventPart);
}
/**
 * `requires:` restatement patterns the #92 additive-only rule discourages — a
 * precondition that merely restates what the action SIGNATURE or CURVED
 * semantics already guarantee, adding no business intent. Three categories,
 * each anchored to phrasings that don't collide with SUBSTANTIVE guards
 * (format constraints, value validity, environment capability, lifecycle
 * state). The marrbox/opus over-restatement signature on large specs (opus
 * idle-meta: 94 requires, dominated by these) is prompt-resistant under the
 * additive prompt → wants a deterministic verifier, analog of
 * `isNonDomainEvent`.
 *
 * Deliberately NOT flagged (substantive — keep): "Token is a signed,
 * non-expired JWT", "File is a unified .json file; ZIP not supported",
 * "Auto-save has been configured with a slot", "localStorage is available",
 * "Invoice status is open" (lifecycle). Auth-presence is intentionally OUT of
 * v1 (debatable; cross-cutting, not CURVED-implicit).
 */
const REQUIRE_PRESENCE_RE = 
// input/parameter presence: "X is provided", "must be supplied", "identifies the Y", "carries a … id"
/\b(is|are|must be|must have been|has been|have been)\s+(provided|supplied|specified|passed|present)\b|\bidentifies the\b|\bcarries\s+(a|an|at least|the)\b[^.]*\b(id|identifier|key|payload)\b/i;
const REQUIRE_EXISTENCE_RE = 
// entity-existence restatement (CURVED-implicit for retrieve/update/delete)
/^\s*(referenced\s+)?[A-Z][\w]*\s+exists\b|\bexists?\b[^.]*\b(with|by)\s+the\s+(given\s+)?(id|identifier|key)\b|\bmust\s+(already\s+)?exist\b/i;
const REQUIRE_CONNINIT_RE = 
// infrastructure connection / init (not domain logic)
/\bconnection\b[^.]*\binitiali[sz]ed\b|\b(database|mongo(?:db)?|postgres|redis|db)\b[^.]*\binitiali[sz]ed\b/i;
/** Which restatement signal(s) a `requires:` entry trips — empty = substantive. */
export function requireRestatementSignals(req) {
    const s = String(req || '').trim();
    if (!s)
        return [];
    const sigs = [];
    if (REQUIRE_PRESENCE_RE.test(s))
        sigs.push('parameter-presence');
    if (REQUIRE_EXISTENCE_RE.test(s))
        sigs.push('entity-existence');
    if (REQUIRE_CONNINIT_RE.test(s))
        sigs.push('connection-init');
    return sigs;
}
/** True if a `requires:` entry merely restates signature / CURVED-implicit
 *  guarantees (parameter-presence / entity-existence / connection-init) rather
 *  than a genuine business precondition. */
export function isExistenceRestatingRequire(req) {
    return requireRestatementSignals(req).length > 0;
}
//# sourceMappingURL=detectors.js.map