Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .claude-plugin/marketplace.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
"name": "fluencyloop",
"source": ".",
"displayName": "FluencyLoop",
"version": "0.3.16",
"version": "0.3.17",
"description": "A per-feature loop that designs, teaches, journals decisions, and assembles PR reviews.",
"author": {
"name": "baokhang83"
Expand Down
2 changes: 1 addition & 1 deletion .claude-plugin/plugin.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"$schema": "https://json.schemastore.org/claude-code-plugin-manifest.json",
"name": "fluencyloop",
"displayName": "FluencyLoop",
"version": "0.3.16",
"version": "0.3.17",
"description": "Stay fluent in the code your AI agent writes with a per-feature design, build, teaching, and review loop.",
"author": {
"name": "baokhang83",
Expand Down
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,15 @@

All notable changes to FluencyLoop are documented here.

## 0.3.17

### Fixed

- Feature guidance now normalizes calibration dimensions before probing, so a recorded familiarity
level such as `jacoco-coverage-gates: familiar` is reused rather than asked again.
- Embedded-diagram guidance now requires a visible SVG title and calculated, padded zone bounds;
the reader preflight rejects a missing title or a node that touches a dashed boundary.

## 0.3.16

### Fixed
Expand Down
19 changes: 14 additions & 5 deletions claude-skills/diagram-design/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,9 +76,12 @@ exists.
### Embedded iframe contract — no scrollbars

The local reader already supplies the figure frame and caption. The embedded document contains only
the diagram: no page title, eyebrow, header, footer, outer `.frame` wrapper, or body padding. Set
`html, body { margin: 0; padding: 0; overflow: hidden; }` and render the SVG with `display: block;
width: 100%; max-width: 100%; height: auto; min-width: 0`. Never add `overflow: auto`,
the diagram: no HTML page title, eyebrow, header, footer, outer `.frame` wrapper, or body padding.
It **must** include a visible, concise title as an SVG `<text>` element near the top. An HTML
`<title>` is metadata, not a visible title. Reserve a dedicated top band of at least 40 SVG units
for it; do not let a zone, connector, or node occupy that band. Set `html, body { margin: 0;
padding: 0; overflow: hidden; }` and render the SVG with `display: block; width: 100%;
max-width: 100%; height: auto; min-width: 0`. Never add `overflow: auto`,
`overflow-x: auto`, or a positive SVG `min-width` to an embedded diagram.

Before handoff, inspect the iframe at the reader's actual dimensions. Its document must satisfy
Expand All @@ -98,14 +101,20 @@ iframe width. This applies to native-renderer candidates and fallback HTML alike
and 8 units of vertical clearance. Widen or heighten the node, use deliberate two-line text, or
choose a shorter faithful label — never let a label escape or clip.
- A node inside a dashed or solid region must leave at least 16 SVG units between its outer stroke
and every region edge. Compute the region from its contents plus that padding; do not set a node
edge equal to the region edge or hide the collision with clipping.
and every region edge. Derive each region *after* placing its nodes: use at least 24 SVG units at
its left, right, and bottom, and reserve at least 40 SVG units above its first node for the zone
label and breathing room. Do not set a node edge equal to a region edge or hide a collision with
clipping. If those bounds do not fit, rearrange the nodes or split the visual; never squeeze a
dashed boundary against a node.
- Check both light and dark themes. If a native-renderer result fails either check, it does not fit
this graph: use the general fallback rather than editing generated HTML or accepting cramped
geometry. This is a design loop, not a one-shot fallback: measure the fallback, revise its
node sizes, text treatment, region bounds, or layout, then measure again after every revision.
Deliver it only after it passes every geometry check. If a clear fallback cannot pass, simplify
the visual or keep the explanation in prose or a table; never ship a failed first pass.
- Inspect the completed reader rendering, not only the source. Confirm that the SVG title is
visible and that every dashed boundary has clear space around its enclosed nodes. A missing title
or even one node visually touching a boundary fails the preflight and requires another revision.

## Non-embedded diagrams

Expand Down
11 changes: 6 additions & 5 deletions claude-skills/diagram-design/references/type-architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,12 @@ Reserve left/right ports for connections that travel primarily horizontally. Ent

**Dashed paths — same routing rules.** Optional, return, async, and passive flows use `stroke-dasharray="4,3"` and a lighter stroke weight (`stroke-width="1"`). Apply the **same orthogonal routing, port-selection, and bridge/hop rules** as solid paths — the dash pattern only communicates semantic weight, not a different routing grammar. When a dashed path and a solid path must cross, bridge the dashed one (it is by definition the less important connection).

**Zone label and containment margin.** Leave ≥16px between the bottom of the zone eyebrow label
and the top of the first enclosed node. Every enclosed node's *outer stroke* must also leave ≥16px
from every zone edge, including the bottom and sides; size the zone from the padded node bounds,
never so a node touches it. A typical top calculation is `zone_y = node_top − 32`, with the label
mask at `zone_y + 4`.
**Zone label and containment margin.** Derive the zone from the placed node rectangles; do not
eyeball the dashed box. Set its left and right edges at least 24px outside the extreme node strokes,
its bottom at least 24px below them, and its top at least 40px above the first node. This gives the
zone label its own band and keeps every node's *outer stroke* clear of every zone edge, including the bottom and sides.
A node must never share or visually touch a dashed boundary. If those padded
bounds do not fit, move or split nodes before drawing the zone.

## Crossing arrows — bridge / hop

Expand Down
20 changes: 15 additions & 5 deletions claude-skills/feature/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,14 @@ person-specific knowledge lives (the repo journal stays person-neutral; see Rule
entirely is fine — you'll *build* it (see §3.4); `fluencyloop calibration init` seeds it. Never
block on it.

**Match calibration dimensions before asking.** Normalize each needed concept and every profile
key to lowercase kebab case: replace every run of non-alphanumeric characters with one `-`, then
trim leading/trailing `-`. Use an exact match on that normalized key. Thus `JaCoCo coverage gates`
matches `jacoco-coverage-gates`. A matched profile value—**including `familiar`**—settles the
familiarity question: apply its teaching depth and do not probe it again. Only a concept with no
normalized profile match is unknown. Do not use loose semantic guessing to manufacture a match,
but never miss an exact normalized match because prose used spaces, case, or punctuation.

**Load the learner's preferences.** Also read `~/.fluencyloop/preferences.md` — a sibling to
`calibration.md` (global, per-developer, **never committed**) that records recurring *workflow*
choices already settled once, so you never re-ask them — e.g. the completion hand-off (commit +
Expand All @@ -150,11 +158,13 @@ time a recurring choice comes up.

**Probe before you dive in.** Continuously estimating the learner's knowledge is critical, and it
starts *before* the first explanation. From the feature's intent and the code, list the domain
concepts this work will actually require, and for each one the knowledge base doesn't already
settle, **ask** — concisely and batched (one tab per concept in Claude Code; one concise, clearly
separated chat prompt in Codex), then wait. For example, before building a Maven plugin: *"Are you familiar with `plugin.xml` and
Mojo objects (`@Mojo` / `AbstractMojo`)?"* — rather than silently guessing and either boring or
losing them. Record the answers into the knowledge base and let them set your opening depth.
concepts this work will actually require, normalize them against the profile as above, and ask only
for the unmatched ones — concisely and batched (one tab per concept in Claude Code; one concise,
clearly separated chat prompt in Codex), then wait. Never ask a familiarity question for a matched
dimension, even when its level is `familiar`; use that level's teaching depth instead. For example,
before building a Maven plugin: *"Are you familiar with `plugin.xml` and Mojo objects (`@Mojo` /
`AbstractMojo`)?"* — rather than silently guessing and either boring or losing them. Record the
answers into the knowledge base and let them set your opening depth.

**Probe neutrally; never make explanation sound like a burden.** Do not ask whether you should
"keep it light," or imply that the developer needs to opt in to an explanation. Ask what they know
Expand Down
2 changes: 1 addition & 1 deletion plugins/fluencyloop/.codex-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "fluencyloop",
"version": "0.3.16",
"version": "0.3.17",
"description": "Stay fluent in the code your AI agent writes with a per-feature design, build, teaching, and review loop.",
"author": {
"name": "baokhang83",
Expand Down
2 changes: 1 addition & 1 deletion plugins/fluencyloop/VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
0.3.16
0.3.17
19 changes: 14 additions & 5 deletions plugins/fluencyloop/skills/diagram-design/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,9 +76,12 @@ exists.
### Embedded iframe contract — no scrollbars

The local reader already supplies the figure frame and caption. The embedded document contains only
the diagram: no page title, eyebrow, header, footer, outer `.frame` wrapper, or body padding. Set
`html, body { margin: 0; padding: 0; overflow: hidden; }` and render the SVG with `display: block;
width: 100%; max-width: 100%; height: auto; min-width: 0`. Never add `overflow: auto`,
the diagram: no HTML page title, eyebrow, header, footer, outer `.frame` wrapper, or body padding.
It **must** include a visible, concise title as an SVG `<text>` element near the top. An HTML
`<title>` is metadata, not a visible title. Reserve a dedicated top band of at least 40 SVG units
for it; do not let a zone, connector, or node occupy that band. Set `html, body { margin: 0;
padding: 0; overflow: hidden; }` and render the SVG with `display: block; width: 100%;
max-width: 100%; height: auto; min-width: 0`. Never add `overflow: auto`,
`overflow-x: auto`, or a positive SVG `min-width` to an embedded diagram.

Before handoff, inspect the iframe at the reader's actual dimensions. Its document must satisfy
Expand All @@ -98,14 +101,20 @@ iframe width. This applies to native-renderer candidates and fallback HTML alike
and 8 units of vertical clearance. Widen or heighten the node, use deliberate two-line text, or
choose a shorter faithful label — never let a label escape or clip.
- A node inside a dashed or solid region must leave at least 16 SVG units between its outer stroke
and every region edge. Compute the region from its contents plus that padding; do not set a node
edge equal to the region edge or hide the collision with clipping.
and every region edge. Derive each region *after* placing its nodes: use at least 24 SVG units at
its left, right, and bottom, and reserve at least 40 SVG units above its first node for the zone
label and breathing room. Do not set a node edge equal to a region edge or hide a collision with
clipping. If those bounds do not fit, rearrange the nodes or split the visual; never squeeze a
dashed boundary against a node.
- Check both light and dark themes. If a native-renderer result fails either check, it does not fit
this graph: use the general fallback rather than editing generated HTML or accepting cramped
geometry. This is a design loop, not a one-shot fallback: measure the fallback, revise its
node sizes, text treatment, region bounds, or layout, then measure again after every revision.
Deliver it only after it passes every geometry check. If a clear fallback cannot pass, simplify
the visual or keep the explanation in prose or a table; never ship a failed first pass.
- Inspect the completed reader rendering, not only the source. Confirm that the SVG title is
visible and that every dashed boundary has clear space around its enclosed nodes. A missing title
or even one node visually touching a boundary fails the preflight and requires another revision.

## Non-embedded diagrams

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,12 @@ Reserve left/right ports for connections that travel primarily horizontally. Ent

**Dashed paths — same routing rules.** Optional, return, async, and passive flows use `stroke-dasharray="4,3"` and a lighter stroke weight (`stroke-width="1"`). Apply the **same orthogonal routing, port-selection, and bridge/hop rules** as solid paths — the dash pattern only communicates semantic weight, not a different routing grammar. When a dashed path and a solid path must cross, bridge the dashed one (it is by definition the less important connection).

**Zone label and containment margin.** Leave ≥16px between the bottom of the zone eyebrow label
and the top of the first enclosed node. Every enclosed node's *outer stroke* must also leave ≥16px
from every zone edge, including the bottom and sides; size the zone from the padded node bounds,
never so a node touches it. A typical top calculation is `zone_y = node_top − 32`, with the label
mask at `zone_y + 4`.
**Zone label and containment margin.** Derive the zone from the placed node rectangles; do not
eyeball the dashed box. Set its left and right edges at least 24px outside the extreme node strokes,
its bottom at least 24px below them, and its top at least 40px above the first node. This gives the
zone label its own band and keeps every node's *outer stroke* clear of every zone edge, including the bottom and sides.
A node must never share or visually touch a dashed boundary. If those padded
bounds do not fit, move or split nodes before drawing the zone.

## Crossing arrows — bridge / hop

Expand Down
20 changes: 15 additions & 5 deletions plugins/fluencyloop/skills/feature/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,14 @@ person-specific knowledge lives (the repo journal stays person-neutral; see Rule
entirely is fine — you'll *build* it (see §3.4); `fluencyloop calibration init` seeds it. Never
block on it.

**Match calibration dimensions before asking.** Normalize each needed concept and every profile
key to lowercase kebab case: replace every run of non-alphanumeric characters with one `-`, then
trim leading/trailing `-`. Use an exact match on that normalized key. Thus `JaCoCo coverage gates`
matches `jacoco-coverage-gates`. A matched profile value—**including `familiar`**—settles the
familiarity question: apply its teaching depth and do not probe it again. Only a concept with no
normalized profile match is unknown. Do not use loose semantic guessing to manufacture a match,
but never miss an exact normalized match because prose used spaces, case, or punctuation.

**Load the learner's preferences.** Also read `~/.fluencyloop/preferences.md` — a sibling to
`calibration.md` (global, per-developer, **never committed**) that records recurring *workflow*
choices already settled once, so you never re-ask them — e.g. the completion hand-off (commit +
Expand All @@ -159,11 +167,13 @@ time a recurring choice comes up.

**Probe before you dive in.** Continuously estimating the learner's knowledge is critical, and it
starts *before* the first explanation. From the feature's intent and the code, list the domain
concepts this work will actually require, and for each one the knowledge base doesn't already
settle, **ask** — concisely and batched (one tab per concept in Claude Code; one concise, clearly
separated chat prompt in Codex), then wait. For example, before building a Maven plugin: *"Are you familiar with `plugin.xml` and
Mojo objects (`@Mojo` / `AbstractMojo`)?"* — rather than silently guessing and either boring or
losing them. Record the answers into the knowledge base and let them set your opening depth.
concepts this work will actually require, normalize them against the profile as above, and ask only
for the unmatched ones — concisely and batched (one tab per concept in Claude Code; one concise,
clearly separated chat prompt in Codex), then wait. Never ask a familiarity question for a matched
dimension, even when its level is `familiar`; use that level's teaching depth instead. For example,
before building a Maven plugin: *"Are you familiar with `plugin.xml` and Mojo objects (`@Mojo` /
`AbstractMojo`)?"* — rather than silently guessing and either boring or losing them. Record the
answers into the knowledge base and let them set your opening depth.

**Probe neutrally; never make explanation sound like a burden.** Do not ask whether you should
"keep it light," or imply that the developer needs to opt in to an explanation. Ask what they know
Expand Down
14 changes: 13 additions & 1 deletion tests/plugin.bats
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,11 @@ for feature_skill_text in [feature_text, codex_feature_text]:
assert "**only after the feature is complete**" in feature_skill_text
assert "Never distill during a slice, after a decision, or as a turn-by-turn summary" in feature_skill_text
assert "fluencyloop calibration show --json" in feature_skill_text
assert "**Match calibration dimensions before asking.**" in feature_skill_text
assert "Thus `JaCoCo coverage gates`\nmatches `jacoco-coverage-gates`." in feature_skill_text
assert "including `familiar`**—settles the\nfamiliarity question" in feature_skill_text
assert "Never ask a familiarity question for a matched\ndimension" in feature_skill_text
assert "Only a concept with no\nnormalized profile match is unknown." in feature_skill_text
assert "docs/fluencyloop/distillations/" in feature_skill_text
assert "**Feature delta**" in feature_skill_text
assert "**no overview rewrite**" in feature_skill_text
Expand Down Expand Up @@ -238,7 +243,10 @@ for diagram_skill_text in [
assert "confirm it renders with no `Diagram\nunavailable` notice" in diagram_skill_text
assert "Do not record or hand off a diagram merely because its file\nexists." in diagram_skill_text
assert "### Embedded iframe contract — no scrollbars" in diagram_skill_text
assert "no page title, eyebrow, header, footer, outer `.frame` wrapper" in diagram_skill_text
assert "no HTML page title, eyebrow, header, footer, outer `.frame` wrapper" in diagram_skill_text
assert "visible, concise title as an SVG `<text>` element" in diagram_skill_text
assert "An HTML\n`<title>` is metadata, not a visible title." in diagram_skill_text
assert "dedicated top band of at least 40 SVG units" in diagram_skill_text
assert "Never add `overflow: auto`,\n`overflow-x: auto`, or a positive SVG `min-width`" in diagram_skill_text
assert "`scrollWidth <= clientWidth` and `scrollHeight <= clientHeight`" in diagram_skill_text
assert "within the reader's fixed 33rem height" in diagram_skill_text
Expand All @@ -249,6 +257,7 @@ for diagram_skill_text in [
assert "This is a design loop, not a one-shot fallback" in diagram_skill_text
assert "then measure again after every revision" in diagram_skill_text
assert "never ship a failed first pass" in diagram_skill_text
assert "A missing title\n or even one node visually touching a boundary fails the preflight" in diagram_skill_text
for full_guide_text in [
read_text(root / "claude-skills" / "diagram-design" / "references" / "full-guide.md"),
read_text(dist / "skills" / "diagram-design" / "references" / "full-guide.md"),
Expand All @@ -262,6 +271,9 @@ for architecture_reference in [
read_text(dist / "skills" / "diagram-design" / "references" / "type-architecture.md"),
]:
assert "**Zone label and containment margin.**" in architecture_reference
assert "Derive the zone from the placed node rectangles; do not\neyeball the dashed box." in architecture_reference
assert "at least 24px outside the extreme node strokes" in architecture_reference
assert "top at least 40px above the first node" in architecture_reference
assert "including the bottom and sides" in architecture_reference
claude_plan_text = read_text(root / "claude-skills" / "plan" / "SKILL.md")
codex_plan_text = read_text(dist / "skills" / "plan" / "SKILL.md")
Expand Down
Loading