Skip to content

feat(flow-next): project glossary + decision records + doc-aware interview — 0.39.0#124

Merged
gmickel merged 15 commits intomainfrom
fn-38-project-glossary-decision-records-and
Apr 30, 2026
Merged

feat(flow-next): project glossary + decision records + doc-aware interview — 0.39.0#124
gmickel merged 15 commits intomainfrom
fn-38-project-glossary-decision-records-and

Conversation

@gmickel
Copy link
Copy Markdown
Owner

@gmickel gmickel commented Apr 30, 2026

Summary

  • GLOSSARY.md at repo root — new project canonical-vocabulary file with flowctl glossary {add,list,read,remove} subcommands, nearest-ancestor walk (tsconfig-style first-match-wins, ceiling at git repo root, 32-level cap), _Avoid_ aliases, multi-line definitions via --definition-file -. Survives rm -rf .flow/ because terminology is the project's, not flow-next's.
  • knowledge/decisions/ memory category — extends categorized memory schema with decision_status lifecycle (proposed → accepted → superseded), superseded_by, alternatives_considered. 1-3 sentence body floor; Considered Options + Consequences optional. Indexed by memory-scout, walked by /flow-next:audit.
  • Doc-aware mode in /flow-next:interview — autodetects when glossary has terms (husks ignored via total_terms > 0) or decisions/ is non-empty. --docs / --no-docs override. Four behaviors: glossary lookup before terminology questions (writes ## Glossary Conflicts spec section), inline glossary write on resolution, decision-record prompt with three-criteria gate + read-back, code/spec contradiction surfaced not silently overwritten.
  • Downstream extensionsdocs-gap-scout scans GLOSSARY.md + knowledge/decisions/. /flow-next:audit Phase 0.5 walks glossary terms (greps code, marks stale on absence, surfaces alias-creep) + Phase 0.1 auto-walks decisions (supersede-not-delete Replace). /flow-next:sync Phase 3b detects glossary renames (rewrite via _Avoid_ aliases) and flags decision overrides read-only (never auto-supersedes).
  • R17/R4 two-tier grep guard — canonical scan in ci_test.sh section 5c (file:line breadcrumbs), mirror scan in sync-codex.sh validation block (count + remediation hint). Catches DDD vocabulary leakage and meta-file references in both source-of-truth and Codex mirror.
  • 17 R-IDs satisfied across 8 tasks, 14 commits. Pre-built Codex mirror regenerated; all 21 skills + 21 agents + hooks.json sync clean.

Test plan

  • plugins/flow-next/scripts/ci_test.sh — 56/56 pass (was 47, +9 for decisions track + R17/R4 guards)
  • plugins/flow-next/scripts/smoke_test.sh — 130/130 pass
  • plugins/flow-next/scripts/glossary_smoke_test.sh — 80/80 pass (new — 25 cases including nearest-ancestor walk, atomic writes, multi-line stdin, fenced-code stripping, husk hygiene, R18 verification)
  • plugins/flow-next/scripts/audit_smoke_test.sh — 41/41 pass (no regression)
  • plugins/flow-next/scripts/prospect_smoke_test.sh — 94/94 pass (no regression)
  • scripts/sync-codex.sh — clean (21 skills, 21 agents, all 14 required openai.yaml, R17 + R4 mirror guards green)
  • R17 grep guard fixture-injection verified (top-level canonical fixture + subdir → mirror propagation both fire correctly)
  • Manual smoke on /flow-next:interview doc-aware autodetect: empty repo → off, one term added → on, husk-after-remove → off (R18 verified by total_terms > 0 check), decisions-only → on
  • Reviewer: spot-check GLOSSARY.md round-trip (addreadremove) in a fresh repo
  • Reviewer: verify flowctl glossary read resolves nearest-ancestor when run from a subdirectory with its own GLOSSARY.md
  • Reviewer: confirm /flow-next:audit Phase 0.5 surfaces _Avoid_ alias creep when an alias appears in code

R-IDs

R1-R18 all satisfied (R18 added during planning to make uninstall-survival testable). Coverage table in .flow/specs/fn-38-project-glossary-decision-records-and.md.

Out of scope (deferred)

Subdir-scoped --scope here flag, slug normalization for special characters, dangling superseded_by ref validation, concurrent-write coordination beyond atomic single-call writes, file-watching cache invalidation, auto-generated glossary-from-code, decision-supersession workflow on audit (surfaces only).

🤖 Generated by /flow-next:work from epic spec at .flow/specs/fn-38-project-glossary-decision-records-and.md

gmickel added 14 commits April 30, 2026 10:25
…rontmatter

- MEMORY_CATEGORIES["knowledge"] gains "decisions" slot; memory init lazy-creates
  knowledge/decisions/.gitkeep via existing directory loop
- New optional fields: decision_status (proposed | accepted | superseded enum),
  superseded_by, alternatives_considered (inline list)
- MEMORY_FIELD_ORDER carries explicit slots after applies_when so writes are
  deterministic across repeated read+write cycles
- validate_memory_frontmatter enum-checks decision_status; argparse choices on
  --decision-status give cli-level rejection too (belt + braces)
- ci_test.sh decisions-track section: round-trip of all three optional fields,
  invalid decision_status rejected (cli + validator), deterministic write order
  across two cycles, lazy-dir-create assertion on memory init
- smoke_test.sh placeholder count 5 → 6 + explicit decisions/.gitkeep check
- memory README template lists decisions/ + new decision-specific optional fields

Task: fn-38-project-glossary-decision-records-and.1
- Add `flowctl glossary {add,list,read,remove}` subcommands
- `find_nearest_glossary` walks cwd → repo root, bounded at git root,
  filesystem boundary (st_dev change), 32-level defensive cap; never
  manually follows symlinks (kernel handles ELOOP)
- `parse_glossary_file` masks fenced code byte-for-byte (spaces in
  place of non-newlines) so heading offsets stay aligned with the
  original text — fenced `## not a heading` lines are not picked up
- `render_glossary_file` always emits `# Glossary` H1 husk so the
  last-term-removal path leaves a recognizable file (R18: project
  state, never delete)
- `add` writes to nearest-ancestor (matches read resolution); creates
  at repo root when no ancestor exists; force a subdir glossary by
  dropping an empty `GLOSSARY.md` first (no `--scope` flag per spec)
- `--definition-file -` reads stdin; `--definition-file <path>` reads
  file; mutually exclusive with `--definition`
- Case-insensitive term match for update + read + remove; whitespace-
  collapsed compare
- New `glossary_smoke_test.sh` (80 cases): bare repo, single-line
  roundtrip, multi-line stdin, mutex flags, empty-input rejection,
  case-insensitive update, multi-term insertion order, subdir-wins,
  ancestor-fallback, repo-root cutoff, 32-level depth cap, atomic-write
  crash simulation, parse roundtrip, _Avoid_ + _Relates to_ roundtrip,
  term removal, husk hygiene, fenced-code stripping, R18 (.flow/
  removal), R4 (no meta-file), R17 (no DDD jargon in help), R15
  (markdown shape), JSON shape

Task: fn-38-project-glossary-decision-records-and.2

Includes T1 plan-sync drift updates to .flow/tasks/...1.md + ...8.md
(memory-scout knowledge-category enumeration drift surfaced by T1
worker; updated by plan-sync but not yet committed).
…cs, four behaviors)

- Husk-aware autodetect via `glossary list --json | jq .total_terms` + decisions count
- `--docs` forces on (lazy-creates root GLOSSARY.md), `--no-docs` forces off
- Behavior (a) phase-zero glossary scan with load-bearing throttle
- Behavior (b) fuzzy-term sharpening writes via `glossary add --definition-file -`
- Behavior (c) code-vs-assertion contradiction surfaces via AskUserQuestion
- Behavior (d) decision-record write with three-criteria gate + read-back
- Glossary-lookup as third Pre-Question Taxonomy axis
- New `## Glossary Conflicts` spec section
- R17 clean: no DDD jargon in canonical or Codex mirror
- sync-codex.sh validation green: no AskUserQuestion leak, all openai.yaml present

Task: fn-38-project-glossary-decision-records-and.3
- Scan list adds `flowctl glossary list --json` (raw `find` fallback) +
  `.flow/memory/knowledge/decisions/`. Husk groups (count: 0) skipped.
- Mapping table gains "Glossary term touched" + "Decision constraint" rows.
- Output example shows term + decision-id flags surfacing.
- Codex mirror regenerated via sync-codex.sh (clean).
- R17: no DDD jargon.

Task: fn-38-project-glossary-decision-records-and.4
- Phase 0.5 (new) — enumerate GLOSSARY.md via flowctl glossary list --json;
  per term, grep tracked code for term + _Avoid_ aliases (case-insensitive,
  whole-word, normalized whitespace); zero hits + zero alias hits → mark stale
  via Edit-tool HTML comment after term heading (no flowctl glossary mark-stale
  exists post-T2); alias hits → alias-creep finding for Phase 3 / report; husk
  files (count: 0) get a single advisory, never deleted.
- Decisions auto-walked — MEMORY_CATEGORIES knowledge:decisions extension
  (T1) means the existing glob picks up knowledge/decisions/*.md without a
  new phase. Documented in workflow.md §0.1.
- Decision-entry calibration in phases.md — judging question shifts to "does
  the constraint that motivated this decision still hold?"; Replace becomes a
  two-step supersession (write new entry; edit old's decision_status:
  superseded + superseded_by; never git rm).
- Phase 5 report grows a Glossary section (Files scanned / Terms scanned /
  Kept / Marked stale / Alias-creep flagged) plus per-term detail and husk
  advisories.
- SKILL.md Forbidden block: explicit prohibitions on git-rm'ing superseded
  decisions, deleting glossary terms, mass code renames from alias-creep,
  inventing flowctl glossary mark-stale.
- Codex mirror regenerated via sync-codex.sh (zero errors; Claude-native
  AskUserQuestion → request_user_input rewrite intact; no DDD vocabulary).

Verification:
- ./scripts/sync-codex.sh — clean
- audit_smoke_test.sh — 41/41 PASS (no regression)
- glossary_smoke_test.sh — 80/80 PASS (no regression)
- R17 grep guard (ubiquitous language|bounded context|domain expert|
  aggregate root) across plugins/flow-next/{skills,agents,commands,codex} +
  flowctl.py — RC=1 (clean)

Task: fn-38-project-glossary-decision-records-and.5
Earlier flowctl done absorbed T4's stale /tmp evidence files because the
Write tool refused to overwrite them. Replace with the actual T5 commit
(ffd9535) + actual T5 test commands.
Sync skill (Step 5/6) now gathers `flowctl glossary list --json` and
`flowctl memory list --track knowledge --category decisions --json`,
passing both as `GLOSSARY_JSON` / `DECISIONS_JSON` to the plan-sync agent.

Plan-sync agent gains Phase 3b:
- 3b.1 glossary-term renames: when a downstream/old spec uses an
  `_Avoid_` alias and the new code uses the canonical term, update the
  spec (replaces alias, leaves a `<!-- Updated by plan-sync: glossary
  rename ... -->` breadcrumb). Reuses `_glossary_term_matches`
  semantics (case-insensitive + whitespace-collapsed).
- 3b.2 decision overrides: when completed-task code touches files
  referenced in an active decision's `Consequences` section in a way
  that contradicts the decision, surface the decision id in the
  summary under "Decision overrides flagged for review". Read-only —
  no auto-supersession (user/audit handles that).

Husks (`count: 0`) and superseded decisions are skipped — no signal.
Both context inputs are best-effort with empty defaults so the agent
prompt stays valid when flowctl returns nothing or fails.

Codex mirror regenerated.

R17 clean. Smoke suites unchanged: 130 + 80 + 41 + 94 + 58 + 74 pass.

Task: fn-38-project-glossary-decision-records-and.6
ci_test.sh: add 5c "Plugin-source hygiene" block scanning
plugins/flow-next/{skills,agents,commands,scripts/flowctl.py} for
R17 forbidden vocabulary and R4 meta-file references; failure
prints offending file:line.

sync-codex.sh: extend validation block with mirror-scoped R17 +
R4 grep guards alongside the existing AskUserQuestion / ToolSearch
hygiene check; failure increments errors and prints count +
remediation hint. Two-tier mirror keeps the mirror's invariants the
sync script's responsibility.

Verified: both tiers pass on current canonical + mirror; injecting
a fixture in skills/flow-next/ trips canonical guard (file:line
breadcrumb) and propagates through sync to trip the mirror guard
(3 R17 hits + 1 R4 hit, exit 1).

Task: fn-38-project-glossary-decision-records-and.7
…ware interview

- CHANGELOG.md: new [flow-next 0.39.0] block (Added / Changed / Notes)
- CLAUDE.md: interview commands list + memory block (decisions category +
  decision-specific fields) + new Glossary section after Prospecting
- README.md (root): version badge bump + what's-new callout to v0.39.0
- plugins/flow-next/README.md: version badge, what's-new, TOC, memory tree
  (decisions/), decisions frontmatter schema, audit + sync extension notes,
  docs-gap-scout note, interview doc-aware mode + Glossary Conflicts template
  section, commands table interview row, autonomous flag table interview row,
  new Project Glossary section
- .flow/usage.md: new Glossary subcommand block
- plugins/flow-next/docs/flowctl.md: available-commands list adds glossary,
  memory category enumeration adds decisions + decision-specific fields,
  new ### glossary section with subcommands + JSON shapes + husk semantics
- plugins/flow-next/agents/memory-scout.md:22 — knowledge category list
  appends decisions (caught by T1 plan-sync)
- .flow/specs/fn-38-...md:238 — fix husk-aware autodetect example to use
  flowctl glossary list --json | jq '.total_terms > 0' instead of plain
  [[ -f GLOSSARY.md ]] (caught by T2 plan-sync)
- plugins/flow-next/codex/agents/memory-scout.toml — regenerated by
  scripts/sync-codex.sh

R17 + R4 grep guards both green (canonical ci_test.sh section 5c +
sync-codex.sh mirror validation). 56 ci_test, 130 smoke, 80 glossary smoke
all pass. No mickel.tech changes (maintainer-only per CLAUDE.md).

Task: fn-38-project-glossary-decision-records-and.8
Task: fn-38-project-glossary-decision-records-and.8
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 527b9da4c7

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +315 to +320
for stripped_match in (avoid_match, relates_match):
if stripped_match is not None:
def_text = (
def_text[: stripped_match.start()]
+ def_text[stripped_match.end() :]
)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Strip glossary metadata lines without stale match offsets

When an entry includes both _Avoid_ and _Relates to_, parse_glossary_file mutates def_text twice using match indices captured from the original body; after the first splice, the second match offsets are no longer valid. Because render_glossary_file writes _Avoid_ before _Relates to_, this common path leaves a truncated _Relates to fragment in definition, so subsequent glossary read/list/add/remove round-trips corrupt the stored definition text.

Useful? React with 👍 / 👎.

@gmickel gmickel merged commit 54dba4e into main Apr 30, 2026
3 checks passed
@gmickel gmickel deleted the fn-38-project-glossary-decision-records-and branch April 30, 2026 19:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant