|
root / docs / reef-feedback-response.md
reef-feedback-response.md markdown 166 lines 11.5 KB

Response: repoman v0.1 Feedback

Source feedback: bugs/reef-feedback.md
Reef version evaluated: 0.5.9
Response date: 2026-04-28
Status: All 8 items accepted — implementation plan in bugs/reef-feedback-implementation-plan.md


Summary

Thank you for the thorough, well-prioritized feedback. Each item is a real friction point and the proposed API shapes are largely on the mark. We accepted all 8 items; no rejections.

Two corrections worth flagging up front:

  • Item 6 is partially out of date. assert_true, assert_false, assert_not_eq_int, assert_not_eq_bool, and assert_not_eq_string already exist on TestRunner in v0.5.9. Genuinely missing: assert_contains and Result-aware assertions (assert_ok_* / assert_err_*). We'll add those.
  • Item 4 sketch references str.substring_from — that function does not exist. The correct call in v0.5.9 is str.substring(s, start, len). The implementation will use the real function; just flagging in case repoman has the workaround coded against a placeholder name.

The rest is accurate as filed.


Item-by-item disposition

1. TOML/JSON encoders missing — HIGH — accepted

Going with the builder API (your option b), not the flat toml_serialize(keys, values, count). Reason: the parser's parallel-array representation cannot losslessly round-trip [[project]] table-arrays — table-array indices are encoded into key names as a parser-internal detail, not a public contract. A flat serialize would be ambiguous on round-trip.

The builder will be a plain struct (not an Active Object — no message-passing overhead needed for single-threaded doc building), modeled on text.stringbuilder. JSON gets a parallel JsonBuilder with the same surface plus begin/end_object / begin/end_array for JSON's mandatory structural nesting.

YAML and INI extensions are out of scope for this batch — repoman doesn't need them, and YAML's nested-table indentation is non-trivial.

Scope: medium (2-3 days). Sprint 4.

2. io.file missing rename and fsync — HIGH — accepted

Both will be added to io.file. rename becomes a thin Reef wrapper delegating to the existing runtime.reef_fs_rename (same C function fs.ops.rename already calls — keeping fs.ops.rename in place because the modules have different audiences; doc cross-references on both will resolve the discoverability concern).

fsync requires a small C runtime addition (reef_fs_fsync(path) in reef_fs.c) plus the FFI bridge plus the user-facing wrapper. Path-based, not handle-based — io.file has no handle type today and adding one would be a much larger redesign. The path-based fsync does an extra open/close, but for the atomic-write recipe that's nanoseconds; the actual disk flush dominates.

Scope: small. Sprint 2.

3. No generic Result[T, E]MEDIUM — accepted

Will create core/result_generic.reef mirroring core/option_generic.reef exactly. Five functions: is_ok[T,E], is_err[T,E], unwrap_ok[T,E], unwrap_err[T,E], unwrap_or[T,E]. The existing core.result (type-specific monomorphizations) stays untouched for one release; deprecation notice and stdlib internal migration follow in a later release.

One thing to validate first: that two-parameter generic enums (enum Ok(T); Err(E)) compile cleanly. option_generic.reef proves single-param works; we'll smoke-test the two-param case before writing function bodies. If we hit a compiler limit, fallback is Result[T] with E fixed to string — still satisfies the use case you described.

The syntax reference's "Future stdlib" note about generic Result will be updated to "Available in core.result_generic" once shipped.

Scope: small. Sprint 3.

4. io.path.expand_home missing — MEDIUM — accepted

Pure-Reef addition to io.path. Behavior: ~$HOME, ~/foo$HOME/foo, anything else → passthrough. If HOME is unset, returns the path unchanged (substituting empty would silently corrupt ~/foo to /foo).

expand_user("~user/...") is deferred to a separate item — it requires a new C runtime helper (getpwnam resolution) and adds a getpwnam_r thread-safety footnote. Your feedback rated it as a "maybe" and we'd rather ship expand_home quickly than couple them. The current expand_home returns unchanged on ~user form so adding expand_user later won't break anything.

Scope: small. Sprint 1.

5. sys.flag no subcommand dispatch — MEDIUM — accepted in two phases

Phase 1 (Sprint 2): flag_parser_from(args: [string]): FlagParser — caller slices argv and feeds it in. parse() reads from the override array when set, otherwise calls sys.args as today. Backward-compatible.

Phase 2 (Sprint 4): Full subcommand_parser API matching cobra/clap conventions. Has open design questions worth resolving with you before implementation:

  • Should subcommand flags be defined before dispatch (each subcommand pre-registered with its own parser) or after (cobra-style, parser handed back from dispatch)?
  • How do global flags (e.g., --verbose before the subcommand token) coexist with per-subcommand flags?
  • Does dispatch intercept --help and route to subcommand-specific usage, or is that the caller's job?

We'll spec Phase 2 against repoman's actual subcommand structure once you reach that part of the build. Phase 1 unblocks you immediately.

Scope: Phase 1 small (~1 day), Phase 2 medium (2-3 days after design pass).

6. test.framework thin assertions — MEDIUM — accepted (with correction)

Already in v0.5.9 (you may have been reading older docs):

  • assert_true(condition, message) — line 130
  • assert_false(condition, message) — line 135
  • assert_not_eq_int/bool/string — lines 178-201

Genuinely missing, will add (Sprint 1):

  • assert_contains_string(haystack, needle, message) — substring; uses existing str.contains
  • assert_ok_int/str/bool — pass when Result*_Ok(_), fail with diagnostic showing the wrapped error
  • assert_err_int/str/bool — pass when Result*_Err(_)

Skipping ResultFloat variants for now (rare in error paths; add on demand). Skipping array-membership assert_contains until generic Result lands — a typed-array version per element type would be 4-5 procs and isn't worth it before generics. The string substring form covers your actual use case.

A test_framework.reef test file (which currently doesn't exist) will be created alongside.

Scope: small. Sprint 1.

7. TOML parser quirks — LOW — accepted in two phases

Phase 1 (Sprint 1) — silent truncation: new toml_parse_status(input, keys, values, max_capacity): int returning -1 when input remains unconsumed AND the cap was hit. Existing toml_parse and toml_parse_sized are untouched (zero breakage). Module docstring will be updated to recommend toml_parse_status for production callers.

json_parse has the identical silent-truncation flaw at a 512-entry cap — we'll mirror the fix as json_parse_status in the same release, given the symmetry.

Phase 2 (Sprint 3) — TomlDoc struct: new ergonomic API replacing the parallel-array threading. Adds TomlDoc struct + toml_parse_doc() + 4 wrapper getters (toml_get_doc, toml_get_int_doc, toml_get_bool_doc, toml_has_key_doc). Coexists with old API for ≥1 release.

Scope: Phase 1 small, Phase 2 small-medium.

8. join_path naming — LOW — accepted (bundled)

Adding fn join(a, b) as the canonical export, leaving join_path as a silent synonym. No deprecation ceremony — Reef has no precedent for one in the stdlib (verified by grep through CHANGELOG; all prior changes were additions or fixes, never renames). A docstring line on join_path notes that join is preferred.

This is the only <verb>_path redundancy in io.pathdirname/basename/extension etc. don't repeat the module name. No coordinated naming pass needed elsewhere.

Scope: ~15 minutes. Bundled with item 4 in Sprint 1.


Sprint plan summary

Detailed plan in bugs/reef-feedback-implementation-plan.md. Top-line sequencing:

Sprint Items Est. effort Outcome
1 4, 6, 7a, 8 (+JSON mirror) ~1.5 days Quick wins; unblocks ~half the friction points
2 2, 5a ~2 days Runtime additions; subcommand-friendly parser
3 3, 7b ~1.5 days Generic Result + ergonomic TomlDoc
4 1, 5b ~5 days Encoders + full subcommand API

Defer indefinitely: expand_user (item 4 follow-on, requires runtime work and only a "maybe" in the feedback).


What you can do today

While we land Sprint 1, you can unblock these items in repoman with minimal hand-rolled glue:

  • expand_home — your sketched workaround works; just substitute str.substring(p, 2, str.length(p) - 2) for the non-existent substring_from.
  • assert_containsrunner.assert_eq_bool(str.contains(haystack, needle), true, message) works as a stopgap.
  • Atomic write — without fsync, the write→rename sequence still gives crash-consistency in the common case (just not power-loss durability). Acceptable for v0.1.
  • TOML truncation detection — call toml_parse_sized(input, keys, values, max_entries) with your own cap and assert the returned count is less than the cap — if equal, you may be truncated.
  • Subcommand dispatch — hand-roll using sys.args.get_args() to extract argv[1] as the subcommand, then call flag_parser() with a different program name per subcommand. Migrate to flag_parser_from(slice) once Sprint 2 ships.

Test plan summary

Each sprint will add tests; the matrix below shows what coverage lands per item:

Item Test file Cases
4 tests/integration/test_path_expand_home.reef bare ~, ~/foo, /abs, relative, empty, ~~double, HOME unset
8 (same as above) path.join(a, b) produces same result as path.join_path(a, b)
6 reef-stdlib/test/test_framework.reef (new) pass + fail paths for every new assertion, verify diagnostic format
7a reef-stdlib/encoding/toml_test.reef (new) exact-cap parse returns count (not -1), over-cap returns -1, under-cap returns count
2 extends examples/test_file_io.reef rename existence check, atomic-write recipe (write→fsync→rename)
5a new tests/integration/test_flag_subcommand.reef flat parse unchanged, sliced parse, tool sub --flag end-to-end
3 new tests/result_generic_test.reef Result[int,string], Result[Point,string] (struct payload — exercises BUG-034 territory)
7b extends toml_test.reef TomlDoc round-trip, truncated flag
1 new tests/encoding_toml_writer_test.reef, encoding_json_writer_test.reef round-trip parse→serialize→parse for all supported types incl. [[project]]
5b extends test_flag_subcommand.reef full tool sub --flag end-to-end via subcommand_parser

Open questions for repoman team

  1. Subcommand structure (item 5b): what does repoman's actual subcommand layout look like? Knowing whether you need global flags before the subcommand token, and whether subcommands can have sub-subcommands (tool group sub --flag), shapes the Phase 2 design.
  2. Result error type (item 3): if generic Result[T, E] two-param form hits a compiler limit, are you okay with Result[T] where E = string? Your feedback example used string errors throughout, so this should be a no-op.
  3. Encoder priority (item 1): TOML or JSON first? You're using both per the feedback, but if one unblocks more of repoman's surface, we'll lead with that.

Reef team — Chris Tusal