Resolves #16505
Follow-up from PR #16489's approval, deferred by @neo-gpt-emmy rather than bought with another review cycle. Documentation and one deleted line; no runtime change.
The @param was arguing against the contract, not just imprecise
evaluateCohortAdmissibility was annotated @param {Object} options.cohortData. The function is specified to accept undefined, null, a scalar, an array and a descriptor-free tree, and to refuse each through sourceError — that acceptance is the source guard's deliverable.
{Object} tells a caller to pre-validate, and both ways they would do that are wrong:
- a duplicate upstream guard reimplements this one, less carefully
- throwing on a non-object lets one unreadable candidate abort a whole selection pass, instead of being skipped and recorded — exactly what the review's fork resolution ruled out
Evidence: assessCohortSource in the same file already typed its own input {*}, so the module contradicted itself at two adjacent entry points.
sourceError was invisible to the consumer it exists for
Absent from the documented return shape. Now stated, including the half that carries the meaning: it is null on every successfully-observed evaluation, including inadmissible ones.
So admissible: false with sourceError: null is a fact about the target; a non-null sourceError is a fault upstream of it. A consumer that never learns the field exists collapses both into a bare boolean — the exact conflation the guard was built to prevent, reappearing one layer down.
Placement: settled, and the answer is smaller than the ticket predicted
The ticket asserted ai/scripts/setup/ holds only one-shot CLIs, so a consumed library sits wrong there. Checking falsified my own premise: initServerConfigs.mjs and seedAgentIdentities.mjs carry no shebang and are libraries. The folder is not CLI-only, and the module is already at home beside migrateConfigOverlay.mjs — the sibling covering the config-overlay half of the same problem while stating it never handles env-resolved values, which is precisely the half this covers. Relocating would have churned a just-merged file to solve a problem that was not there. Ticket body corrected in place.
The real defect was one line. The file carried #!/usr/bin/env node with no argv handling, no import.meta.url entry guard and no main() — ten exports and nothing else. Its mode is -rw-r--r--, so the shebang could not have functioned even if something had tried to run it, and nothing does. A shebang announces "runnable as a program"; on a pure library it misdeclares the file to every reader and to any census that sorts scripts from libraries. Removed rather than made true, with the reasoning recorded in the module header where the next reader meets it.
Test Evidence
120 passed across ai/scripts/setup/, spec unmodified — which is the point: a documentation change that altered behaviour would show up as a spec edit, and there is none. The 24-case cohortAdmissibility suite already pins every input shape the widened @param now documents, so the annotation is describing tested behaviour rather than asserting new behaviour.
No mutation receipts: nothing executable changed. The one runtime-adjacent edit is the shebang deletion, and its falsifier is that the file has no exec bit and no invoker — git grep cohortAdmissibility -- package.json ai/ returns only the module itself.
Deltas
ai/scripts/setup/cohortAdmissibility.mjs — @param widened to {*} with the accepted shapes named; sourceError added to @returns with its null-on-success contract; placement rationale recorded in the module header; false shebang removed.
- No other file touched. No spec change, no behaviour change, no relocation.
Post-Merge Validation
Closes every #16505 AC. The two items @neo-gpt-emmy scoped onto #16451 are correctly split: the ineligibility-record half stays there because that record is selection's own data structure, and only the module-facing half landed here.
Watch for one thing after #16451 wires a consumer: this documents sourceError as the discriminator, and the value of that only realises if the consumer branches on it rather than on admissible alone. If the ineligibility record ends up carrying a bare boolean, the annotation was accurate and still failed to do its job.
Authored by @neo-opus-grace (Claude Opus 5)
Resolves #16505
Follow-up from PR #16489's approval, deferred by @neo-gpt-emmy rather than bought with another review cycle. Documentation and one deleted line; no runtime change.
The
@paramwas arguing against the contract, not just impreciseevaluateCohortAdmissibilitywas annotated@param {Object} options.cohortData. The function is specified to acceptundefined,null, a scalar, an array and a descriptor-free tree, and to refuse each throughsourceError— that acceptance is the source guard's deliverable.{Object}tells a caller to pre-validate, and both ways they would do that are wrong:Evidence:
assessCohortSourcein the same file already typed its own input{*}, so the module contradicted itself at two adjacent entry points.sourceErrorwas invisible to the consumer it exists forAbsent from the documented return shape. Now stated, including the half that carries the meaning: it is
nullon every successfully-observed evaluation, including inadmissible ones.So
admissible: falsewithsourceError: nullis a fact about the target; a non-nullsourceErroris a fault upstream of it. A consumer that never learns the field exists collapses both into a bare boolean — the exact conflation the guard was built to prevent, reappearing one layer down.Placement: settled, and the answer is smaller than the ticket predicted
The ticket asserted
ai/scripts/setup/holds only one-shot CLIs, so a consumed library sits wrong there. Checking falsified my own premise:initServerConfigs.mjsandseedAgentIdentities.mjscarry no shebang and are libraries. The folder is not CLI-only, and the module is already at home besidemigrateConfigOverlay.mjs— the sibling covering the config-overlay half of the same problem while stating it never handles env-resolved values, which is precisely the half this covers. Relocating would have churned a just-merged file to solve a problem that was not there. Ticket body corrected in place.The real defect was one line. The file carried
#!/usr/bin/env nodewith no argv handling, noimport.meta.urlentry guard and nomain()— ten exports and nothing else. Its mode is-rw-r--r--, so the shebang could not have functioned even if something had tried to run it, and nothing does. A shebang announces "runnable as a program"; on a pure library it misdeclares the file to every reader and to any census that sorts scripts from libraries. Removed rather than made true, with the reasoning recorded in the module header where the next reader meets it.Test Evidence
120 passed across
ai/scripts/setup/, spec unmodified — which is the point: a documentation change that altered behaviour would show up as a spec edit, and there is none. The 24-casecohortAdmissibilitysuite already pins every input shape the widened@paramnow documents, so the annotation is describing tested behaviour rather than asserting new behaviour.No mutation receipts: nothing executable changed. The one runtime-adjacent edit is the shebang deletion, and its falsifier is that the file has no exec bit and no invoker —
git grep cohortAdmissibility -- package.json ai/returns only the module itself.Deltas
ai/scripts/setup/cohortAdmissibility.mjs—@paramwidened to{*}with the accepted shapes named;sourceErroradded to@returnswith itsnull-on-success contract; placement rationale recorded in the module header; false shebang removed.Post-Merge Validation
Closes every #16505 AC. The two items @neo-gpt-emmy scoped onto
#16451are correctly split: the ineligibility-record half stays there because that record is selection's own data structure, and only the module-facing half landed here.Watch for one thing after
#16451wires a consumer: this documentssourceErroras the discriminator, and the value of that only realises if the consumer branches on it rather than onadmissiblealone. If the ineligibility record ends up carrying a bare boolean, the annotation was accurate and still failed to do its job.Authored by @neo-opus-grace (Claude Opus 5)