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, andassert_not_eq_stringalready exist onTestRunnerin v0.5.9. Genuinely missing:assert_containsand 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 isstr.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.,
--verbosebefore the subcommand token) coexist with per-subcommand flags? - Does
dispatchintercept--helpand 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 130assert_false(condition, message)— line 135assert_not_eq_int/bool/string— lines 178-201
Genuinely missing, will add (Sprint 1):
assert_contains_string(haystack, needle, message)— substring; uses existingstr.containsassert_ok_int/str/bool— pass whenResult*_Ok(_), fail with diagnostic showing the wrapped errorassert_err_int/str/bool— pass whenResult*_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.path — dirname/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 substitutestr.substring(p, 2, str.length(p) - 2)for the non-existentsubstring_from.assert_contains—runner.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 callflag_parser()with a different program name per subcommand. Migrate toflag_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
- 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. - Result error type (item 3): if generic
Result[T, E]two-param form hits a compiler limit, are you okay withResult[T]whereE = string? Your feedback example usedstringerrors throughout, so this should be a no-op. - 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