feat: Add spec_path field — link tasks to git documentation

- spec_path column added to tasks table
- API POST/PATCH support spec_path field
- Discord /tasks detail view shows '📄 Full Spec' link to Gitea when spec exists
- Backfilled 7 existing tasks with spec directory paths

Architecture: Database tracks state, Git stores documentation, they link to each other.

Chronicler #78 | firefrost-services
This commit is contained in:
Claude (Chronicler #78)
2026-04-11 14:38:24 +00:00
parent b8f9926e9b
commit aca7669243
2 changed files with 11 additions and 5 deletions

View File

@@ -166,6 +166,11 @@ async function handleTaskDetail(interaction, taskNumber) {
embed.addFields({ name: 'Tags', value: t.tags.map(tag => `\`${tag}\``).join(' '), inline: false });
}
if (t.spec_path) {
const specUrl = `https://git.firefrostgaming.com/firefrost-gaming/firefrost-operations-manual/src/branch/master/${t.spec_path}`;
embed.addFields({ name: '📄 Full Spec', value: `[View in Gitea](${specUrl})`, inline: false });
}
if (t.completed_at) {
const completedDate = new Date(t.completed_at).toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' });
embed.addFields({ name: 'Completed', value: `${completedDate}${t.completed_by ? ` by ${t.completed_by}` : ''}`, inline: false });

View File

@@ -454,7 +454,7 @@ router.get('/tasks/summary', async (req, res) => {
// POST /api/internal/tasks — Create a new task
router.post('/tasks', async (req, res) => {
try {
const { title, description, priority, owner, tags } = req.body;
const { title, description, priority, owner, tags, spec_path } = req.body;
if (!title) return res.status(400).json({ error: 'Title required' });
// Auto-assign next task number
@@ -462,9 +462,9 @@ router.post('/tasks', async (req, res) => {
const taskNumber = maxResult.rows[0].next;
const result = await db.query(
`INSERT INTO tasks (task_number, title, description, priority, owner, tags)
VALUES ($1, $2, $3, $4, $5, $6) RETURNING *`,
[taskNumber, title, description || null, priority || 'medium', owner || 'unassigned', tags || null]
`INSERT INTO tasks (task_number, title, description, priority, owner, tags, spec_path)
VALUES ($1, $2, $3, $4, $5, $6, $7) RETURNING *`,
[taskNumber, title, description || null, priority || 'medium', owner || 'unassigned', tags || null, spec_path || null]
);
console.log(`📋 [Tasks] Created #${taskNumber}: ${title}`);
res.json({ success: true, task: result.rows[0] });
@@ -478,7 +478,7 @@ router.post('/tasks', async (req, res) => {
router.patch('/tasks/:id', async (req, res) => {
try {
const { id } = req.params;
const { status, priority, owner, title, description, completed_by } = req.body;
const { status, priority, owner, title, description, completed_by, spec_path } = req.body;
const updates = [];
const params = [];
@@ -489,6 +489,7 @@ router.patch('/tasks/:id', async (req, res) => {
if (owner) { p++; updates.push(`owner = $${p}`); params.push(owner); }
if (title) { p++; updates.push(`title = $${p}`); params.push(title); }
if (description !== undefined) { p++; updates.push(`description = $${p}`); params.push(description); }
if (spec_path !== undefined) { p++; updates.push(`spec_path = $${p}`); params.push(spec_path); }
// Auto-set completed_at when marking done
if (status === 'done') {