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
19 changes: 17 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,8 @@ It is designed to solve three problems:

`gsync` creates a shared coordination loop:

- the team sets a `2-week` goal for higher-level direction
- the team sets a `3-day` target for short-term focus
- the team sets a `2-week` goal by pushing a plan with `--goal 2week`
- the team sets a `3-day` target by pushing a plan with `--goal 3day`
- each person publishes active plans with summaries, ownership, touched surfaces, and status
- teammates can pull and inspect each other's plans
- agents can ingest synced context before generating new plans
Expand Down Expand Up @@ -129,8 +129,11 @@ gsync login --key <seat-key>
gsync sync --last 20
cat ~/.gsync/CONTEXT.md
gsync memory reviewer-context # compiled memory bundle; fails closed if sync is stale
gsync report bug --title "Login copy is confusing" --body "The error did not tell me whether the seat key or network was wrong."
gsync plan pull <id> # only if a summary looks relevant
gsync plan push my-plan.md # create or update canonical plan
gsync plan push my-plan.md --goal 3day # push plan and set as 3-day target
gsync plan push my-plan.md --goal 2week # push plan and set as 2-week goal
gsync plan update <id> --note "milestone or blocker"
gsync plan review <id> --pr https://github.com/org/repo/pull/123
gsync plan merged <id>
Expand Down Expand Up @@ -158,3 +161,15 @@ Key behavior:
- Memories are append-only markdown entries; older entries stay visible in the dashboard and compiled reviewer context.
- `gsync sync` recompiles the compiled memory bundle.
- If memory changes after your last sync, `gsync memory reviewer-context` fails closed until you sync again.

## Product Feedback

gsync also has a built-in feedback queue for bugs and feature requests about gsync itself. This is not meant to replace your main GitHub issue tracker; it is an in-product inbox for early product feedback while the coordination workflow is still evolving.

```bash
gsync report bug --title "Join flow is unclear" --body "I expected the CLI to tell me whether the join code expired or was mistyped." --severity medium
gsync report feature --title "Show plan diffs" --body "I want to compare plan revisions before pulling the latest markdown."
gsync report list
```

The dashboard exposes the same queue in the `reports` tab so the team has one internal source of truth for product feedback coming from active use.
41 changes: 28 additions & 13 deletions SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,19 @@ Use this to:
- Check if any active plan already touches the files you're about to edit — if so, coordinate before starting
- Avoid duplicating work that's already in progress or just merged

If gsync itself feels broken, confusing, or unintuitive while you are using it, file feedback right away:

```bash
gsync report bug --title "<short title>" --body "<what broke, why it was confusing, and what should have happened>"
gsync report feature --title "<short title>" --body "<what you wanted to do, what was missing, and why it matters>"
```

Guidance for agents:
- Be honest. If something feels rough, say so plainly.
- Be lenient in tone. The goal is useful product feedback, not venting.
- Report product issues in gsync itself, not bugs in the user's application or repo.
- Prefer specific reproduction details over vague complaints.

Important: `gsync sync` no longer mirrors every full plan file locally. It gives you a summary index first. Pull full markdown plans only when they are relevant.

Important: approved company memory is now compiled separately from the summary context. `gsync memory reviewer-context` prints the approval-gated company brief, project brief, decision log, and plan context bundle that reviewer agents should use. If approved memory changed after your last sync, this command fails closed and tells you to rerun `gsync sync` first.
Expand Down Expand Up @@ -227,6 +240,9 @@ Now take the output of your gstack plan and register it with gsync so your teamm

```bash
gsync plan push my-plan.md
# or, to also set this plan as the 3-day target or 2-week goal:
gsync plan push my-plan.md --goal 3day
gsync plan push my-plan.md --goal 2week
```

Recommended `my-plan.md` shape:
Expand Down Expand Up @@ -348,22 +364,18 @@ Do this before starting the next task. Stale "review" plans pollute the team's C

## Goal Management

Goals should be updated when the team explicitly decides to change direction — not unilaterally by one agent or engineer. When you do update them, use specific, measurable language:
Goals are set by pushing a plan with `--goal 3day` or `--goal 2week`. Goals should be updated when the team explicitly decides to change direction — not unilaterally by one agent or engineer. When you do update them, use specific, measurable language in the plan summary:

```bash
# Too vague — bad
gsync goals set-2week --goal "improve the product"

# Concrete and measurable — good
gsync goals set-2week --goal "Ship multiplayer beta to 50 invite-only users with real-time presence, cursor tracking, and conflict-free editing by April 18"
# Push a plan and set it as the 3-day target
gsync plan push my-plan.md --goal 3day

# Too vague — bad
gsync goals set-3day --goal "work on the backend"

# Specific enough to coordinate around — good
gsync goals set-3day --goal "Merge WebSocket presence layer into staging and get sign-off from design on the cursor UI by Wednesday EOD"
# Push a plan and set it as the 2-week goal
gsync plan push my-plan.md --goal 2week
```

The plan summary becomes the goal text visible to teammates and in CONTEXT.md. The full plan body is available via `gsync plan pull <id>`.

---

## Reference: All Commands
Expand All @@ -381,14 +393,17 @@ gsync plan pull <id> # fetch full markdown plan into ~/.gsync/plans/
gsync plan pull <id> --metadata-only # print summary metadata only
gsync plan pull <id> --stdout # print summary metadata + canonical markdown body
gsync plan push my-plan.md # create/update canonical markdown plan from file
gsync plan push my-plan.md --goal 3day # push plan and set as 3-day target
gsync plan push my-plan.md --goal 2week # push plan and set as 2-week goal
gsync plan update <id> ... # add a progress note (see Step 3)
gsync plan review <id> --pr <url> # link plan to PR, move to review status
gsync plan merged <id> # close a plan after PR merges
gsync goals set-2week --goal "..." # update 2-week goal
gsync goals set-3day --goal "..." # update 3-day target
gsync memory draft --title "..." --body "..." # create a planning conversation draft
gsync memory approve <draft-id> --to companyBrief|projectBrief|decisionLog # promote approved memory
gsync memory reviewer-context # print the compiled, fail-closed approved-memory bundle
gsync report bug --title "..." --body "..." --severity medium # report a gsync bug
gsync report feature --title "..." --body "..." # report a gsync feature request
gsync report list # inspect recent gsync product feedback
```

---
Expand Down
112 changes: 112 additions & 0 deletions dashboard/src/App.css
Original file line number Diff line number Diff line change
Expand Up @@ -1119,6 +1119,118 @@ button.feed-item {
background: rgba(59, 47, 30, 0.03);
}

.reports-page {
display: flex;
flex-direction: column;
gap: 16px;
}

.reports-kicker {
font-family: var(--font-display);
font-size: 12px;
letter-spacing: 0.4px;
color: var(--text-muted);
}

.reports-list {
display: grid;
gap: 14px;
}

.report-card {
border: 1px solid var(--border);
border-radius: var(--radius);
background: rgba(250, 247, 240, 0.9);
box-shadow: var(--shadow-sm);
padding: 18px 18px 16px;
}

.report-card__meta,
.report-card__footer {
display: flex;
align-items: center;
gap: 8px;
flex-wrap: wrap;
}

.report-card__title {
font-family: var(--font-display);
font-size: 16px;
font-weight: 500;
color: var(--text);
margin: 10px 0 8px;
}

.report-card__body {
color: var(--text);
white-space: pre-wrap;
}

.report-card__footer {
margin-top: 14px;
font-size: 12px;
color: var(--text-muted);
justify-content: space-between;
}

.report-kind,
.report-severity {
display: inline-flex;
align-items: center;
justify-content: center;
border-radius: 999px;
padding: 3px 8px;
font-family: var(--font-display);
font-size: 10px;
letter-spacing: 0.7px;
text-transform: uppercase;
}

.report-kind--bug {
color: #9d2d19;
background: rgba(196, 67, 43, 0.12);
border: 1px solid rgba(196, 67, 43, 0.24);
}

.report-kind--feature {
color: #175d78;
background: rgba(23, 93, 120, 0.1);
border: 1px solid rgba(23, 93, 120, 0.22);
}

.report-severity--low {
color: var(--text-muted);
background: rgba(138, 125, 107, 0.12);
border: 1px solid rgba(138, 125, 107, 0.18);
}

.report-severity--medium {
color: #8a4f08;
background: rgba(240, 122, 58, 0.12);
border: 1px solid rgba(240, 122, 58, 0.24);
}

.report-severity--high {
color: #9d2d19;
background: rgba(232, 93, 38, 0.12);
border: 1px solid rgba(232, 93, 38, 0.28);
}

.report-time {
font-family: var(--font-display);
font-size: 11px;
color: var(--text-muted);
margin-left: auto;
}

.reports-empty {
border: 1px dashed var(--border);
border-radius: var(--radius);
background: rgba(250, 247, 240, 0.7);
padding: 20px;
color: var(--text-muted);
}

/* Memory panel header row */
.memory-panel__header {
display: flex;
Expand Down
36 changes: 9 additions & 27 deletions dashboard/src/components/GoalBar.jsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
import { useState, useEffect } from 'react';
import { collection, doc, onSnapshot } from 'firebase/firestore';
import { doc, onSnapshot } from 'firebase/firestore';
import { db } from '../firebase.js';
import { relativeTime } from '../utils.js';
import { findGoalLinkedPlan } from '../lib/planTags.js';

export default function GoalBar({ teamId, onSelectPlan }) {
const [twoWeek, setTwoWeek] = useState(null);
const [threeDay, setThreeDay] = useState(null);
const [plans, setPlans] = useState([]);
const [error, setError] = useState(null);
const [selected, setSelected] = useState(null);
const [, setTick] = useState(0);
Expand All @@ -34,32 +32,20 @@ export default function GoalBar({ teamId, onSelectPlan }) {
};
}, [teamId]);

useEffect(() => {
const unsubPlans = onSnapshot(
collection(db, 'teams', teamId, 'plans'),
(snap) => setPlans(snap.docs.map((d) => ({ id: d.id, ...d.data() }))),
(err) => setError(err.message),
);

return unsubPlans;
}, [teamId]);

function openGoal(type, label, data) {
const linkedPlan = findGoalLinkedPlan(plans, type, data?.content);
if (linkedPlan?.id && onSelectPlan) {
onSelectPlan(linkedPlan.id);
function openGoal(label, data) {
if (data?.planId && onSelectPlan) {
onSelectPlan(data.planId);
return;
}

setSelected({ label, data });
}

return (
<>
<div className="goal-bar">
{error && <div className="error-banner" style={{ color: '#fff', background: '#e53e3e', padding: '8px 12px', borderRadius: '8px', marginBottom: '8px' }}>{error}</div>}
<GoalCard label="2-week goal" variant="primary" data={twoWeek} onClick={() => openGoal('2week', '2-week goal', twoWeek)} />
<GoalCard label="3-day target" variant="secondary" data={threeDay} onClick={() => openGoal('3day', '3-day target', threeDay)} />
<GoalCard label="2-week goal" variant="primary" data={twoWeek} onClick={() => openGoal('2-week goal', twoWeek)} />
<GoalCard label="3-day target" variant="secondary" data={threeDay} onClick={() => openGoal('3-day target', threeDay)} />
</div>
{selected && (
<GoalDetail label={selected.label} data={selected.data} onClose={() => setSelected(null)} />
Expand All @@ -74,7 +60,7 @@ function GoalCard({ label, variant, data, onClick }) {
<div className="goal-label">{label}</div>
{data ? (
<>
<div className="goal-content">{data.content}</div>
<div className="goal-content">{data.summary}</div>
<div className="goal-meta">
Updated {relativeTime(data.updatedAt)}
{data.updatedBy && ` by ${data.updatedBy}`}
Expand All @@ -99,12 +85,8 @@ function GoalDetail({ label, data, onClose }) {
{data ? (
<>
<div className="modal-section">
<div className="section-label">content</div>
<div className="section-value">{data.content}</div>
</div>
<div className="modal-section">
<div className="section-label">linked plan</div>
<div className="section-value">No matching canonical plan was found for this goal yet.</div>
<div className="section-label">summary</div>
<div className="section-value">{data.summary}</div>
</div>
<div className="modal-section">
<div className="section-label">last updated</div>
Expand Down
Loading
Loading