plan: form-a-scalar-param-mode-carveout — M1 fieldtest friction+spec_gap#1 docs-honesty tidy
This commit is contained in:
@@ -0,0 +1,463 @@
|
||||
# form_a.md scalar-parameter mode carve-out — Implementation Plan
|
||||
|
||||
> **Parent spec:** `docs/specs/2026-05-18-fieldtest-embedding-abi-m1.md`
|
||||
> (findings **[friction]** "The published authoring rule steers an LLM
|
||||
> away from the only spelling that works for scalar export params" +
|
||||
> **[spec_gap]#1** "form_a.md mandates a mode on every fn param;
|
||||
> scalar params accept (and require) no mode"). Orchestrator design
|
||||
> decision recorded in `docs/roadmap.md` P1 entry "[todo] form_a.md
|
||||
> scalar-parameter mode carve-out". This is a **docs-honesty tidy** in
|
||||
> the `docs-honesty-lint` class, NOT a milestone — there is no
|
||||
> `docs/specs/<milestone>.md`; the fieldtest spec + roadmap entry are
|
||||
> the design carrier.
|
||||
>
|
||||
> **For agentic workers:** REQUIRED SUB-SKILL: use `skills/implement`
|
||||
> to run this plan. Steps use `- [ ]` checkboxes for tracking.
|
||||
|
||||
**Goal:** Reconcile `crates/ailang-core/specs/form_a.md` and
|
||||
`docs/DESIGN.md` §"Embedding ABI (M1)" with the *actual* checker
|
||||
behaviour — scalar (`Int`/`Bool`/`Unit`/`Str`) `(fn …)` parameters
|
||||
take **no** mode and are written bare, mirroring the existing
|
||||
return-type carve-out — and pin the corrected state against
|
||||
regrowth.
|
||||
|
||||
**Architecture:** Pure documentation + a regression pin. **Zero**
|
||||
language / checker / codegen change: the iteration only edits two
|
||||
docs and adds one `#[test]`. The checker behaviour being documented
|
||||
is already shipped and unchanged (recon Section D: the linearity
|
||||
pass in `crates/ailang-check/src/linearity.rs:324-329` skips entirely
|
||||
unless *all* params carry an explicit mode; a mode on a scalar makes
|
||||
the checker hold the primitive to linear discipline, so an ordinary
|
||||
reuse trips `use-after-consume` / `consume-while-borrowed` pointed at
|
||||
the body — `crates/ailang-check/src/linearity.rs:757` / `:1074`). The
|
||||
fix is RED-first: the pin is written first asserting the post-edit
|
||||
state, fails (new carve-out absent + old unconditional phrasing still
|
||||
present), then the doc edits turn it green.
|
||||
|
||||
**Tech Stack:** `crates/ailang-core/specs/form_a.md`,
|
||||
`docs/DESIGN.md`, `crates/ailang-core/tests/docs_honesty_pin.rs`
|
||||
(uses `ailang_core::FORM_A_SPEC` — the `include_str!`'d constant at
|
||||
`crates/ailang-core/src/lib.rs:120`, the single canonical in-code
|
||||
handle for that spec, also used by `tests/spec_drift.rs`; the
|
||||
DESIGN.md half reuses the file's existing `read("docs/DESIGN.md")`
|
||||
helper). `spec_drift.rs` is **not** a lockstep partner — recon
|
||||
confirmed all its `FORM_A_SPEC` anchors are short keyword tokens
|
||||
(`(own` / `(borrow` survive because the carve-out *adds*, never
|
||||
removes, the mode wrappers); no `spec_drift.rs` assertion pins any
|
||||
altered text.
|
||||
|
||||
**Scope: four form_a.md sites + one DESIGN.md insertion + one pin.**
|
||||
Recon found the unconditional rule restated in four places inside
|
||||
`form_a.md`; all four are in scope because a docs-honesty tidy whose
|
||||
thesis is "the doc mirrors only the true current state, consistently"
|
||||
cannot fix some and leave others contradicting them (that ships the
|
||||
exact internal inconsistency the pin class forbids). Site 5
|
||||
(`form_a.md:521-524`, a few-shot annotation) already states the
|
||||
correct rule using the canonical "heap-shaped / primitive value
|
||||
type" vocabulary — it is left **verbatim** (no churn), and the new
|
||||
carve-out text deliberately reuses its framing so the document is
|
||||
internally consistent end to end.
|
||||
|
||||
---
|
||||
|
||||
## Files this plan creates or modifies
|
||||
|
||||
- Modify: `crates/ailang-core/specs/form_a.md:96-98` — site 1, the
|
||||
`### Function` prose mode sentence.
|
||||
- Modify: `crates/ailang-core/specs/form_a.md:230` — site 2, the
|
||||
grammar-block `implicit mode` comment.
|
||||
- Modify: `crates/ailang-core/specs/form_a.md:347-352` — site 3,
|
||||
"Schema invariants" item 1.
|
||||
- Modify: `crates/ailang-core/specs/form_a.md:381-383` — site 4, the
|
||||
"Forgetting mode annotations" Pitfalls bullet.
|
||||
- Modify: `docs/DESIGN.md:2285` — insert the canonical bare-scalar
|
||||
export-param Form-A example after the §"Embedding ABI (M1)"
|
||||
scalar-constraint paragraph, before `## Data model` (2287).
|
||||
- Test: `crates/ailang-core/tests/docs_honesty_pin.rs:12` /
|
||||
`:26` — add `use ailang_core::FORM_A_SPEC;` and a new `#[test] fn
|
||||
form_a_scalar_param_carveout_present_and_old_rule_absent`.
|
||||
|
||||
Files that do NOT change (verified by recon, recorded so the
|
||||
implementer does not "tidy" them): `crates/ailang-core/specs/form_a.md:521-524`
|
||||
(already-correct few-shot annotation, left verbatim);
|
||||
`crates/ailang-core/tests/spec_drift.rs` (not a lockstep partner);
|
||||
`crates/ailang-core/src/lib.rs` (only defines the const);
|
||||
any `crates/ailang-check/**` source (zero behaviour change).
|
||||
|
||||
---
|
||||
|
||||
## Task 1: RED — write the regression pin asserting the post-edit state
|
||||
|
||||
**Files:**
|
||||
- Test: `crates/ailang-core/tests/docs_honesty_pin.rs`
|
||||
|
||||
- [ ] **Step 1: Add the `FORM_A_SPEC` import**
|
||||
|
||||
In `crates/ailang-core/tests/docs_honesty_pin.rs`, the current line 12 is:
|
||||
|
||||
```rust
|
||||
use std::fs;
|
||||
```
|
||||
|
||||
Replace it with:
|
||||
|
||||
```rust
|
||||
use ailang_core::FORM_A_SPEC;
|
||||
use std::fs;
|
||||
```
|
||||
|
||||
(`FORM_A_SPEC` is `pub const FORM_A_SPEC: &str` at
|
||||
`crates/ailang-core/src/lib.rs:120`; this integration test is in the
|
||||
`ailang-core` crate's `tests/` dir and may use the crate's public
|
||||
API, exactly as `tests/spec_drift.rs:17` does.)
|
||||
|
||||
- [ ] **Step 2: Append the new pin test**
|
||||
|
||||
Append this `#[test]` fn to the END of
|
||||
`crates/ailang-core/tests/docs_honesty_pin.rs` (after the closing
|
||||
`}` of `prose_roundtrip_md_has_no_wunschdenken`, which is the last
|
||||
item, currently line 100):
|
||||
|
||||
```rust
|
||||
|
||||
#[test]
|
||||
fn form_a_scalar_param_carveout_present_and_old_rule_absent() {
|
||||
// form_a.md is read via the canonical include_str! const, not a
|
||||
// re-derived path: single source of truth, compile-checked, no
|
||||
// stale-path bug. norm() collapses the docs' hard line-wrap so
|
||||
// every asserted substring below is the single-spaced form
|
||||
// (planner Step-5 item 6: line-wrap is structurally discharged,
|
||||
// the substrings need not be contiguous in the .md source).
|
||||
let f = norm(FORM_A_SPEC);
|
||||
let d = norm(&read("docs/DESIGN.md"));
|
||||
|
||||
// --- ABSENT: the four old unconditional phrasings must be gone ---
|
||||
assert!(!f.contains("Mode annotations on every `(fn ...)` parameter."),
|
||||
"form_a.md item 1 must no longer head with the unconditional 'every (fn ...) parameter' rule (site 3)");
|
||||
assert!(!f.contains("All parameters of a `(fn ...)` MUST carry a mode annotation"),
|
||||
"form_a.md ### Function prose must no longer state the unconditional all-params rule (site 1)");
|
||||
assert!(!f.contains("(DO NOT USE in new (fn ...) defs)"),
|
||||
"form_a.md grammar block must no longer blanket-forbid the bare (implicit-mode) form (site 2)");
|
||||
assert!(!f.contains("Wrap every `(fn ...)` parameter in `(own ...)` or `(borrow ...)`."),
|
||||
"form_a.md Pitfalls bullet must no longer state the unconditional 'wrap every parameter' imperative (site 4)");
|
||||
|
||||
// --- PRESENT: the scalar-parameter carve-out, the four sites ---
|
||||
assert!(f.contains("A scalar (non-heap-shaped) parameter or return takes **no** mode: write it bare, e.g. `(con Int)`."),
|
||||
"form_a.md item 1 must state the scalar-parameter carve-out symmetrically with the return-type carve-out (site 3)");
|
||||
assert!(f.contains("a scalar (non-heap-shaped: `(con Int)`, `(con Bool)`, `(con Unit)`, `(con Str)`) takes **no** mode and is written bare"),
|
||||
"form_a.md ### Function prose must carry the scalar carve-out (site 1)");
|
||||
assert!(f.contains("; no mode — REQUIRED for scalars (Int/Bool/Unit/Str), rejected for heap-shaped"),
|
||||
"form_a.md grammar block comment must state the bare form is required for scalars (site 2)");
|
||||
assert!(f.contains("Putting a mode on a scalar parameter."),
|
||||
"form_a.md Pitfalls must carry the inverse pitfall — over-wrapping a scalar (site 4)");
|
||||
|
||||
// --- PRESENT: the DESIGN.md §\"Embedding ABI (M1)\" mirror ---
|
||||
assert!(d.contains("Export parameters are written **bare**: a scalar type carries no `own`/`borrow` mode"),
|
||||
"DESIGN.md §Embedding ABI (M1) must mirror the canonical bare-scalar export-param rule");
|
||||
}
|
||||
```
|
||||
|
||||
- [ ] **Step 3: Run the pin — it MUST FAIL (this is the RED)**
|
||||
|
||||
Run: `cargo test -p ailang-core --test docs_honesty_pin form_a_scalar_param_carveout_present_and_old_rule_absent`
|
||||
|
||||
Expected: **FAIL**. The first failing assertion is the first ABSENT
|
||||
check — `form_a.md item 1 must no longer head with the unconditional
|
||||
'every (fn ...) parameter' rule (site 3)` — because the old phrasing
|
||||
`Mode annotations on every \`(fn ...)\` parameter.` is still present
|
||||
in `form_a.md` (the doc edits are Tasks 2–3). This proves the pin
|
||||
discriminates the pre-edit state.
|
||||
|
||||
- [ ] **Step 4: Confirm the rest of the file's pins are unaffected**
|
||||
|
||||
Run: `cargo test -p ailang-core --test docs_honesty_pin`
|
||||
|
||||
Expected: the four pre-existing tests
|
||||
(`design_md_has_no_wunschdenken`, `design_md_has_no_doc_archaeology`,
|
||||
`design_md_present_tense_anchors_present`,
|
||||
`prose_roundtrip_md_has_no_wunschdenken`) **PASS**; only
|
||||
`form_a_scalar_param_carveout_present_and_old_rule_absent` **FAILS**.
|
||||
("test result: FAILED. 4 passed; 1 failed".)
|
||||
|
||||
---
|
||||
|
||||
## Task 2: form_a.md — the four carve-out sites
|
||||
|
||||
**Files:**
|
||||
- Modify: `crates/ailang-core/specs/form_a.md`
|
||||
|
||||
Each step Reads nothing extra — the verbatim current text is given
|
||||
as `old` and the replacement as `new`. The implementer applies each
|
||||
as an exact string replacement.
|
||||
|
||||
- [ ] **Step 1: Site 1 — `### Function` prose (lines 96-98)**
|
||||
|
||||
Replace, verbatim:
|
||||
|
||||
old:
|
||||
```
|
||||
`type` is a `(fn-type ...)` (possibly wrapped in `(forall ...)` for
|
||||
polymorphic defs). All parameters of a `(fn ...)` MUST carry a mode
|
||||
annotation — see *Modes* below.
|
||||
```
|
||||
|
||||
new:
|
||||
```
|
||||
`type` is a `(fn-type ...)` (possibly wrapped in `(forall ...)` for
|
||||
polymorphic defs). A *heap-shaped* parameter or return of a
|
||||
`(fn ...)` MUST carry a mode annotation; a scalar (non-heap-shaped:
|
||||
`(con Int)`, `(con Bool)`, `(con Unit)`, `(con Str)`) takes **no**
|
||||
mode and is written bare — see *Modes* below.
|
||||
```
|
||||
|
||||
- [ ] **Step 2: Site 2 — grammar-block comment (line 230)**
|
||||
|
||||
Replace, verbatim (single line):
|
||||
|
||||
old:
|
||||
```
|
||||
TYPE ; implicit mode (DO NOT USE in new (fn ...) defs)
|
||||
```
|
||||
|
||||
new:
|
||||
```
|
||||
TYPE ; no mode — REQUIRED for scalars (Int/Bool/Unit/Str), rejected for heap-shaped
|
||||
```
|
||||
|
||||
- [ ] **Step 3: Site 3 — "Schema invariants" item 1 (lines 347-352)**
|
||||
|
||||
Replace, verbatim:
|
||||
|
||||
old:
|
||||
```
|
||||
1. **Mode annotations on every `(fn ...)` parameter.** Every type in
|
||||
the `(params ...)` clause of a `(fn ...)` definition's
|
||||
`(fn-type ...)` MUST be wrapped in `(own T)` or `(borrow T)`. The
|
||||
return type MUST also carry a mode whenever the type is heap-shaped
|
||||
(i.e. anything other than `(con Int)`, `(con Bool)`, `(con Unit)`,
|
||||
`(con Str)`). Implicit mode on a `(fn ...)` def is rejected.
|
||||
```
|
||||
|
||||
new:
|
||||
```
|
||||
1. **Mode annotations on every heap-shaped `(fn ...)` parameter.** A
|
||||
parameter type in the `(params ...)` clause of a `(fn ...)`
|
||||
definition's `(fn-type ...)`, and the return type, MUST be wrapped
|
||||
in `(own T)` or `(borrow T)` whenever the type is heap-shaped
|
||||
(i.e. anything other than `(con Int)`, `(con Bool)`, `(con Unit)`,
|
||||
`(con Str)`). A scalar (non-heap-shaped) parameter or return takes
|
||||
**no** mode: write it bare, e.g. `(con Int)`. Scalars are
|
||||
primitive value types, not linear resources; putting a mode on a
|
||||
scalar makes the checker hold the primitive to linear discipline
|
||||
(single use under `own`, no use while a `borrow` is live), so an
|
||||
ordinary scalar reused in the body (`sample * sample`) trips a
|
||||
body-pointing `use-after-consume` / `consume-while-borrowed`
|
||||
rather than a mode error. Implicit (omitted) mode on a
|
||||
*heap-shaped* `(fn ...)` parameter or return is rejected.
|
||||
```
|
||||
|
||||
- [ ] **Step 4: Site 4 — Pitfalls bullet (lines 381-383)**
|
||||
|
||||
Replace, verbatim:
|
||||
|
||||
old:
|
||||
```
|
||||
- **Forgetting mode annotations.** `(fn-type (params (con List)) ...)`
|
||||
is accepted by the parser but rejected by the checker. Wrap every
|
||||
`(fn ...)` parameter in `(own ...)` or `(borrow ...)`.
|
||||
```
|
||||
|
||||
new:
|
||||
```
|
||||
- **Forgetting mode annotations on a heap-shaped parameter.**
|
||||
`(fn-type (params (con List)) ...)` is accepted by the parser but
|
||||
rejected by the checker. Wrap every *heap-shaped* `(fn ...)`
|
||||
parameter in `(own ...)` or `(borrow ...)`.
|
||||
- **Putting a mode on a scalar parameter.** The inverse trap:
|
||||
`(fn-type (params (own (con Int))) ...)` is wrong — scalars
|
||||
(`Int`/`Bool`/`Unit`/`Str`) take no mode, write them bare
|
||||
`(con Int)`. A mode on a scalar makes the checker treat the
|
||||
primitive as a linear resource, so an ordinary reuse trips a
|
||||
body-pointing `use-after-consume` / `consume-while-borrowed`
|
||||
instead of pointing at the signature.
|
||||
```
|
||||
|
||||
- [ ] **Step 5: Run the pin — form_a.md half now GREEN, DESIGN half still RED**
|
||||
|
||||
Run: `cargo test -p ailang-core --test docs_honesty_pin form_a_scalar_param_carveout_present_and_old_rule_absent`
|
||||
|
||||
Expected: still **FAIL**, but now the *only* failing assertion is the
|
||||
last one — `DESIGN.md §Embedding ABI (M1) must mirror the canonical
|
||||
bare-scalar export-param rule` (the four form_a.md ABSENT + four
|
||||
form_a.md PRESENT assertions now all pass; DESIGN.md is edited in
|
||||
Task 3). If any form_a.md assertion still fails, an exact-string
|
||||
replacement above did not match — re-read the named line range and
|
||||
fix the replacement, do not weaken the assertion.
|
||||
|
||||
- [ ] **Step 6: Confirm spec_drift.rs is unaffected (it is not a lockstep partner)**
|
||||
|
||||
Run: `cargo test -p ailang-core --test spec_drift`
|
||||
|
||||
Expected: **PASS**, all 8 tests
|
||||
(`spec_mentions_every_term_variant`,
|
||||
`spec_mentions_every_pattern_variant`,
|
||||
`spec_mentions_every_type_variant`,
|
||||
`spec_mentions_every_literal_variant`,
|
||||
`spec_mentions_every_def_kind`, `spec_mentions_mode_keywords`,
|
||||
`spec_mentions_tail_variants`, `spec_mentions_suppress_clause`).
|
||||
`spec_mentions_mode_keywords` pins only the bare `(own` / `(borrow`
|
||||
tokens, which the carve-out keeps; this run confirms the recon
|
||||
finding that no `spec_drift.rs` assertion couples to the altered
|
||||
text.
|
||||
|
||||
---
|
||||
|
||||
## Task 3: DESIGN.md §"Embedding ABI (M1)" mirror
|
||||
|
||||
**Files:**
|
||||
- Modify: `docs/DESIGN.md:2285`
|
||||
|
||||
- [ ] **Step 1: Insert the canonical bare-scalar export-param example**
|
||||
|
||||
In `docs/DESIGN.md`, the §"Embedding ABI (M1)" scalar-constraint
|
||||
paragraph ends at line 2285 (`The value/record layout freeze is
|
||||
M3.`), followed by a blank line (2286) and `## Data model` (2287).
|
||||
|
||||
Replace, verbatim:
|
||||
|
||||
old:
|
||||
```
|
||||
change this C signature; do not treat the M1 signature as frozen.
|
||||
The value/record layout freeze is M3.
|
||||
|
||||
## Data model
|
||||
```
|
||||
|
||||
new:
|
||||
```
|
||||
change this C signature; do not treat the M1 signature as frozen.
|
||||
The value/record layout freeze is M3.
|
||||
|
||||
Export parameters are written **bare**: a scalar type carries no
|
||||
`own`/`borrow` mode (modes apply only to heap-shaped types, which the
|
||||
scalar-only rule above forbids at an export boundary anyway). The
|
||||
canonical M1 export shape:
|
||||
|
||||
```
|
||||
(fn step
|
||||
(export "backtest_step")
|
||||
(type
|
||||
(fn-type
|
||||
(params (con Int) (con Int))
|
||||
(ret (con Int))))
|
||||
(params state sample)
|
||||
(body
|
||||
(app + state (app * sample sample))))
|
||||
```
|
||||
|
||||
## Data model
|
||||
```
|
||||
|
||||
(This Form-A snippet is the `step` fn of the public
|
||||
`examples/embed_backtest_step.ail` corpus fixture — recon confirmed
|
||||
lines 3-11 — so the example is corpus-grounded, not minted.)
|
||||
|
||||
- [ ] **Step 2: Run the pin — now fully GREEN**
|
||||
|
||||
Run: `cargo test -p ailang-core --test docs_honesty_pin form_a_scalar_param_carveout_present_and_old_rule_absent`
|
||||
|
||||
Expected: **PASS**. All ABSENT + PRESENT assertions hold.
|
||||
|
||||
- [ ] **Step 3: Full docs_honesty_pin suite GREEN**
|
||||
|
||||
Run: `cargo test -p ailang-core --test docs_honesty_pin`
|
||||
|
||||
Expected: **PASS**, "test result: ok. 5 passed; 0 failed" (the four
|
||||
pre-existing tests + the new carve-out pin).
|
||||
|
||||
---
|
||||
|
||||
## Task 4: Regression gate — zero behavioural drift
|
||||
|
||||
**Files:** (none — verification only)
|
||||
|
||||
- [ ] **Step 1: Full `ailang-core` test suite**
|
||||
|
||||
Run: `cargo test -p ailang-core`
|
||||
|
||||
Expected: **PASS**, no test regressed. In particular `spec_drift`
|
||||
(8) and `docs_honesty_pin` (5) green; all pre-existing `ailang-core`
|
||||
tests green. Record the total ("N passed; 0 failed").
|
||||
|
||||
- [ ] **Step 2: Workspace-wide no-behavioural-drift assertion**
|
||||
|
||||
This iteration changes only two docs and adds one test — no
|
||||
language / checker / codegen byte changes. The full workspace suite
|
||||
must therefore not lose any passing test.
|
||||
|
||||
Run: `cargo test --workspace 2>&1 | grep -E "test result:" | awk '{p+=$4} END {print "TOTAL_PASSED="p}'`
|
||||
|
||||
Expected: `TOTAL_PASSED=` a number **≥ the count on `main` HEAD before
|
||||
this iteration** (the only delta is **+1**, the new
|
||||
`form_a_scalar_param_carveout_present_and_old_rule_absent`). If the
|
||||
total is lower than the pre-iteration baseline, a pre-existing test
|
||||
broke — STOP and investigate; a docs+pin iteration cannot regress a
|
||||
behavioural test, so any drop is a real problem, not an expected
|
||||
cost. (Unfiltered suite + explicit pass-count per planner Step-5
|
||||
item 8 — "nothing ran" cannot masquerade as "nothing regressed".)
|
||||
|
||||
- [ ] **Step 3: Confirm zero source-code change**
|
||||
|
||||
Run: `git diff --stat -- crates/ailang-check crates/ailang-codegen crates/ailang-core/src crates/ailang-surface`
|
||||
|
||||
Expected: **empty output** (no diff). The only modified paths are
|
||||
`crates/ailang-core/specs/form_a.md`, `docs/DESIGN.md`, and
|
||||
`crates/ailang-core/tests/docs_honesty_pin.rs`. Any source-code diff
|
||||
under those crate `src/` trees is out of scope for this tidy and a
|
||||
defect — STOP.
|
||||
|
||||
---
|
||||
|
||||
## Self-review (planner Step 5)
|
||||
|
||||
1. **Spec coverage:** [friction] + [spec_gap]#1 both resolved —
|
||||
form_a.md carve-out (Task 2, four sites) + DESIGN.md mirror
|
||||
(Task 3) + anti-regression pin (Task 1). Roadmap entry's
|
||||
"symmetrically with the existing return-type carve-out" satisfied
|
||||
(item 1 new text scopes params to heap-shaped exactly as the
|
||||
pre-existing return-type clause does). ✔
|
||||
2. **Placeholder scan:** no "TBD/TODO/implement later/similar to/add
|
||||
appropriate". The literal string `; no mode — REQUIRED for
|
||||
scalars …` and the test phrases are content, not placeholders. ✔
|
||||
3. **Type/name consistency:** test fn
|
||||
`form_a_scalar_param_carveout_present_and_old_rule_absent` named
|
||||
identically in Task 1 Step 2/3, Task 2 Step 5, Task 3 Step 2.
|
||||
`FORM_A_SPEC`, `norm`, `read` match the actual file. ✔
|
||||
4. **Step granularity:** every step is one exact replacement or one
|
||||
command, 2-5 min. ✔
|
||||
5. **No commit steps:** none. ✔
|
||||
6. **Pin/replacement substring contiguity:** the pin `norm()`s both
|
||||
docs and every asserted substring is written single-spaced; the
|
||||
`norm()` whitespace-collapse **structurally discharges** the
|
||||
line-wrap family (the docs-honesty-lint precedent's `norm()` is
|
||||
reused verbatim, not a fresh keep-on-one-line constraint). Every
|
||||
PRESENT substring appears single-spaced-contiguously inside the
|
||||
exact `new` replacement bodies in Tasks 2–3 (verified: e.g. "A
|
||||
scalar (non-heap-shaped) parameter or return takes **no** mode:
|
||||
write it bare, e.g. `(con Int)`." is one clause in the Task 2
|
||||
Step 3 `new` block; line-wrap between "takes" and "**no**" is
|
||||
collapsed by `norm()`). Every ABSENT substring is verbatim
|
||||
contiguous in the corresponding `old` block and verbatim **gone**
|
||||
from the `new` block. ✔
|
||||
7. **Compile-gate vs. deferred-caller ordering:** N/A — no signature
|
||||
change, no crate-wide compile break; the only Rust change is one
|
||||
additive `#[test]` + one `use`. Task ordering is RED-pin → doc
|
||||
edits → green, the docs-honesty-lint pattern. ✔
|
||||
8. **Verification-command filter strings resolve:** `--test
|
||||
docs_honesty_pin` and `--test spec_drift` are real test-target
|
||||
files (recon-confirmed); the filter
|
||||
`form_a_scalar_param_carveout_present_and_old_rule_absent` is the
|
||||
exact name of the test added in Task 1 (not guessed from a
|
||||
feature word). Task 4 Step 2 uses the **unfiltered** workspace
|
||||
suite + an explicit `TOTAL_PASSED` count assertion so "0 ran"
|
||||
cannot read as "0 regressed". ✔
|
||||
Reference in New Issue
Block a user